-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for multiple color modes #18
Conversation
Awesome.. 🎉 Will be interesting how many functional variables we end up needing. Another thought is: Can we get away with keeping the scales “private”. Like we would use them for the functional variables, but don’t add it to a theme. So that people don’t just start to use the color scale directly and it becomes more unpredictable to change the values. This might have the effect of needing lots more functional variables. Like |
@simurai Yeah, I think you're right here, allowing direct access to the scale values is probably a road to a maintainability nightmare. Do you think we'd ever need access to the scale for one-off things? For example, if a color doesn't work quite right for a certain feature we could override it for a specific mode, e.g. .my-thing {
color: var(--some-functional-color);
@include mode("dark") {
// fix up the shade for this mode
color: var(--some-scale-color);
}
} We could also update stylelint to catch incorrect usage as well. |
575ec39
to
18b6150
Compare
Hopefully that doesn't happen too often, but yeah, probably can't be avoided entirely. There is always some edge cases to be found. 😆 For dotcom, we could also keep these overrides in a separate folder, similar what we currently do with |
Alright, as of the latest commits, things have changed somewhat. Flexible mode typesPrimer Primitives now supports mode definitions for any kind of mode data, not just color modes. For example, we could have Mode compilationMode definitions are compiled to SCSS (with CSS vars), JSON, TypeScript, and JavaScript (via TypeScript compilation). Mode definitionsModes are still defined in individual SCSS files inside For example, here is the beginning of the light color mode definition (note that $export: (
scale: $scale,
// Pure functional
text: (
primary: $gray-900,
secondary: $gray-600,
tertiary: $gray-500,
placeholder: $gray-300,
link-primary: $blue-500,
link-secondary: $gray-900,
link-accent: $blue-500,
warning: $tbd,
danger: $tbd,
success: $tbd,
),
ic: (
primary: $gray-500,
secondary: $gray-400,
tertiary: $orange-600,
// ... Definition formattingWhen building the primitives, maps and variables are translated differently in CSS and JSON/JS. When at all possible, use maps to group related colors together. For example, given the following mode definition: $export: (
// this is a map
text: (
primary: $black,
)
// this is a plain value
text-secondary: $gray-900,
) The CSS output for each would look the same: --text-primary: #...;
--text-secondary: #...; but the JS and JSON output would be different: {
text: {
primary: "..."
},
"textSecondary": "#..."
} The former style, with the nested map, is favored over the latter style, with the entire variable spelled out, so that JS and JSON consumers can consume values using a syntax like ListsCurrently in Primer CSS, we have colors defined by 100s, like this: $blue-000: ...;
$blue-100: ...;
$blue-200: ...; In order to support multiple environments, these are now built as lists, and the compiled CSS will use the index of the list as part of the variable name. For example: $blue: (#..., #..., #..., #..., #...,); This will get compiled in CSS to: --scale-blue-0: #...;
--scale-blue-1: #...;
--scale-blue-2: #...; and to a plain array of strings in JSON/JS. |
That's super nice not having to write out the entire name. Also should help with readability having these groups.
I assume the reason was to allow adding "in-betweens" without having to change the rest of the names.. like adding a $blue-000: ...;
$blue-100: ...;
$blue-150: ...;
$blue-200: ...; But we haven't done that so far, so maybe fine to just use the indexes. If we really wanna add more later, some |
Yes, I think you're right all around, although the old format doesn't work well for the JS version. Since we shouldn't be using the scale values directly very much (at all???) I'm hoping it won't be a huge issue. |
@BinaryMuse I wonder if something more specific like |
Yeah, that definitely makes sense — although I think it's likely we will not be exposing the scale directly (though we may change our minds 🙈) |
@simurai I made a few changes to the build script:
|
Added the dark colors 36490c8 but reversed the order of the scale: Initially just as a shortcut because it allowed me to skip making changes to the rest of dark.scss. But now I'm wondering if we should keep it like this? Option A$gray-900: #24292e; // looks dark in light.scss
$gray-900: #F0F6FC; // looks light in dark.scss
text-primary: $gray-900, // in light.scss
text-primary: $gray-900, // in dark.scss
color: var(--color-text-primary); // in Primer CSS/dotcom It would also allow us to use the scales like $gray-200: #e1e4e8; // looks light in light.scss
$gray-200: #21262D; // looks dark in dark.scss
scale-grey-2: $gray-200, // in light.scss
scale-grey-2: $gray-200, // in dark.scss
background-color: var(--color-scale-grey-2); // in Primer CSS/dotcom So you can think of it like this:
Option BWithout flipping the order of the scale for dark, all the $gray-200: #e1e4e8; // looks light in light.scss
$gray-700: #21262D; // looks dark in dark.scss
my-component-bg: $gray-200, // in light.scss
my-component-bg: $gray-700, // in dark.scss
background-color: var(--color-my-component-bg); // in Primer CSS/dotcom Eventually we still wanna make tweaks here and there by adding custom variables, but using option A we already have a high chance of making it "usable". Risks
@colebemis shared this tweet: With option A we could quickly get to the 2nd version and still be able to tweak it to make it look like the 3rd version. /cc @primer/design-systems |
@BinaryMuse There is something interesting when mixing colors with other units, like this case for shadow: (
small: 0 1px 0 rgba($black, 0.04),
) it compiles to: 👇 --color-shadow-small-0: 0;
--color-shadow-small-1: 1px;
--color-shadow-small-2: 0;
--color-shadow-small-3: rgba(27,31,35,0.04); Not a lot of combinations work but something that seems to work is: shadow: (
shadow-small: 0 1px 0 rgba($black, 0.04),
), 👇 --color-shadow-shadow-small: 0 1px 0 rgba(27,31,35,0.04); Or |
Ah, sorry about that. This is tricky to do in SCSS because we represent arrays of values as SCSS lists, and a property like |
Another open question: Currently we replace variables and utility classes that have a reference to "dark/light" with functional names: It's hard to come up with a good functional name. And adding custom variables and classes like Should we keep the current color utility classes and just migrate SASS variables like The mini guide for "using/adding colors" would then look like this:
There is a risk that we skip 2-4 and only do 5 or 6. But that might still be better than "mis-using" functional variables and using |
Having done the
In addition to a few shared app variables, we could also consider having "single use" variables that can be used with a mixin: // SCSS
.my-class {
@include color-mode(color, gray-2, gray-7); // property, light, dark
}
// output CSS
[data-color-mode="light"] .my-class {
--color: var(--color-scale-gray-2);
}
[data-color-mode="dark"] .my-class {
--color: var(--color-scale-gray-7);
}
.my-class {
color: var(--color);
} Not sure if that's possible? But when authoring new styles, you don't have to think about a variable name that probably is only used once and check if it that name is still available. It would get auto generated based on class name and property and just toggles the value depending on the color mode. It also avoids having lots of merge conflicts in a big |
I was afraid of this, but it's good feedback. I think the alternative you specified is probably a good middle ground. Another option that you and I have discussed that I'd like to bring to the larger group is the idea of removing Primer Primitives and including the infrastructure introduced by this PR in Primer CSS itself. This would mean one less hoop to jump through to add or change color variables, making work in Primer CSS (where we generally first add and iterate on new components) much easier. However, projects like Primer React would need to depend on Primer CSS as a source of truth instead of Primer Primitives. Functionally they would be equivalent, and bundle sizes wouldn't be different, so it'd be more of an organization thing. Another option would be some sort of monorepo for Primer CSS/Primer Primitives, but we've experimented with monorepos before and decided to move away from them. /cc @primer/ds-core for 💭s |
Made the changes here to match the ones on light.scss
Update dark.scss
Co-authored-by: simurai <[email protected]>
To match light.scss
Added an "auto scale" e66591e. This allows using the existing scale variables (e.g. |
This PR introduces color modes to Primer Primitives. Modes are defined in SCSS files in
data/modes/
, and at build-time these are converted into SCSS files with CSS variables for use in Primer CSS, and JSON files (for now) for use in Primer Components and other non-CSS based systems.The SCSS files in
data/modes/
that define color modes must adhere to the following rules:$scale
map that defines the colors of the scale$functional
map that defines the functional colorsOtherwise, authors of the color mode SCSS files may use any imports, functions, mixins, and the like, just as in any other SCSS file.
[Edit] See my comment below for updated information.
This is a MAJOR breaking change and should NOT be merged until otherwise specified.
TODO
$gray-500
->$gray-5
mix-blend-mode: multiply;
to be changed to a different value in dark mode.icon-folder
to something that doesn't start withicon-
. Theicon-
prefix is reserved for only global variables.autocomplete-bg