Skip to content

Commit

Permalink
Parse CSS system colors
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Feb 23, 2024
1 parent 96ea996 commit 2983614
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 6 deletions.
48 changes: 47 additions & 1 deletion node/ast.d.ts
Expand Up @@ -637,7 +637,7 @@ export type Token =
*
* Each color space is represented as a struct that implements the `From` and `Into` traits for all other color spaces, so it is possible to convert between color spaces easily. In addition, colors support [interpolation](#method.interpolate) as in the `color-mix()` function.
*/
export type CssColor = CurrentColor | RGBColor | LABColor | PredefinedColor | FloatColor | LightDark;
export type CssColor = CurrentColor | RGBColor | LABColor | PredefinedColor | FloatColor | LightDark | SystemColor;
export type CurrentColor = {
type: "currentcolor";
};
Expand Down Expand Up @@ -962,6 +962,52 @@ export type LightDark = {
light: CssColor;
type: "light-dark";
};
/**
* A CSS [system color](https://drafts.csswg.org/css-color/#css-system-colors) keyword.
*/
export type SystemColor =
| "accentcolor"
| "accentcolortext"
| "activetext"
| "buttonborder"
| "buttonface"
| "buttontext"
| "canvas"
| "canvastext"
| "field"
| "fieldtext"
| "graytext"
| "highlight"
| "highlighttext"
| "linktext"
| "mark"
| "marktext"
| "selecteditem"
| "selecteditemtext"
| "visitedtext"
| "activeborder"
| "activecaption"
| "appworkspace"
| "background"
| "buttonhighlight"
| "buttonshadow"
| "captiontext"
| "inactiveborder"
| "inactivecaption"
| "inactivecaptiontext"
| "infobackground"
| "infotext"
| "menu"
| "menutext"
| "scrollbar"
| "threeddarkshadow"
| "threedface"
| "threedhighlight"
| "threedlightshadow"
| "threedshadow"
| "window"
| "windowframe"
| "windowtext";
/**
* A color value with an unresolved alpha value (e.g. a variable). These can be converted from the modern slash syntax to older comma syntax. This can only be done when the only unresolved component is the alpha since variables can resolve to multiple tokens.
*/
Expand Down
3 changes: 2 additions & 1 deletion scripts/build-prefixes.js
Expand Up @@ -327,7 +327,8 @@ let mdnFeatures = {
fontStyleObliqueAngle: mdn.css.properties['font-style']['oblique-angle'].__compat.support,
fontWeightNumber: mdn.css.properties['font-weight'].number.__compat.support,
fontStretchPercentage: mdn.css.properties['font-stretch'].percentage.__compat.support,
lightDark: mdn.css.types.color['light-dark'].__compat.support
lightDark: mdn.css.types.color['light-dark'].__compat.support,
accentSystemColor: mdn.css.types.color['system-color'].accentcolor_accentcolortext.__compat.support,
};

for (let key in mdn.css.types.length) {
Expand Down
27 changes: 27 additions & 0 deletions src/compat.rs
Expand Up @@ -6,6 +6,7 @@ use crate::targets::Browsers;
#[derive(Clone, Copy, PartialEq)]
pub enum Feature {
AbsFunction,
AccentSystemColor,
AfarListStyleType,
AmharicAbegedeListStyleType,
AmharicListStyleType,
Expand Down Expand Up @@ -3286,6 +3287,32 @@ impl Feature {
return false;
}
}
Feature::AccentSystemColor => {
if let Some(version) = browsers.firefox {
if version < 6750208 {
return false;
}
}
if let Some(version) = browsers.safari {
if version < 1049856 {
return false;
}
}
if let Some(version) = browsers.ios_saf {
if version < 1049856 {
return false;
}
}
if browsers.android.is_some()
|| browsers.chrome.is_some()
|| browsers.edge.is_some()
|| browsers.ie.is_some()
|| browsers.opera.is_some()
|| browsers.samsung.is_some()
{
return false;
}
}
Feature::QUnit => {
if let Some(version) = browsers.chrome {
if version < 4128768 {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Expand Up @@ -15919,6 +15919,7 @@ mod tests {
minify_test(".foo { color: hsla(100 100% 50% / .8) }", ".foo{color:#5f0c}");
minify_test(".foo { color: transparent }", ".foo{color:#0000}");
minify_test(".foo { color: currentColor }", ".foo{color:currentColor}");
minify_test(".foo { color: ButtonBorder }", ".foo{color:buttonborder}");
minify_test(".foo { color: hwb(194 0% 0%) }", ".foo{color:#00c4ff}");
minify_test(".foo { color: hwb(194 0% 0% / 50%) }", ".foo{color:#00c4ff80}");
minify_test(".foo { color: hwb(194 0% 50%) }", ".foo{color:#006280}");
Expand Down
122 changes: 118 additions & 4 deletions src/values/color.rs
Expand Up @@ -64,6 +64,8 @@ pub enum CssColor {
#[cfg_attr(feature = "visitor", skip_type)]
#[cfg_attr(feature = "serde", serde(with = "LightDark"))]
LightDark(Box<CssColor>, Box<CssColor>),
/// A [system color](https://drafts.csswg.org/css-color/#css-system-colors) keyword.
System(SystemColor),
}

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -361,7 +363,7 @@ impl CssColor {
// below and including the authored color space, and remove the ones that aren't
// compatible with our browser targets.
let mut fallbacks = match self {
CssColor::CurrentColor | CssColor::RGBA(_) | CssColor::Float(..) => return ColorFallbackKind::empty(),
CssColor::CurrentColor | CssColor::RGBA(_) | CssColor::Float(..) | CssColor::System(..) => return ColorFallbackKind::empty(),
CssColor::LAB(lab) => match &**lab {
LABColor::LAB(..) | LABColor::LCH(..) if should_compile!(targets, LabColors) => {
ColorFallbackKind::LAB.and_below()
Expand Down Expand Up @@ -456,7 +458,8 @@ impl IsCompatible for CssColor {
},
CssColor::LightDark(light, dark) => {
Feature::LightDark.is_compatible(browsers) && light.is_compatible(browsers) && dark.is_compatible(browsers)
}
},
CssColor::System(system) => system.is_compatible(browsers)
}
}
}
Expand Down Expand Up @@ -500,8 +503,13 @@ impl<'i> Parse<'i> for CssColor {
"currentcolor" => CssColor::CurrentColor,
"transparent" => CssColor::RGBA(RGBA::transparent()),
_ => {
let (r, g, b) = parse_named_color(value).map_err(|_| location.new_unexpected_token_error(token.clone()))?;
CssColor::RGBA(RGBA { red: r, green: g, blue: b, alpha: 255 })
if let Ok((r, g, b)) = parse_named_color(value) {
CssColor::RGBA(RGBA { red: r, green: g, blue: b, alpha: 255 })
} else if let Ok(system_color) = SystemColor::parse_string(&value) {
CssColor::System(system_color)
} else {
return Err(location.new_unexpected_token_error(token.clone()))
}
}
}),
Token::Function(ref name) => parse_color_function(location, name.clone(), input),
Expand Down Expand Up @@ -603,6 +611,7 @@ impl ToCss for CssColor {
dark.to_css(dest)?;
dest.write_char(')')
}
CssColor::System(system) => system.to_css(dest),
}
}
}
Expand Down Expand Up @@ -2881,6 +2890,7 @@ macro_rules! color_space {
CssColor::Float(float) => (**float).into(),
CssColor::CurrentColor => return Err(()),
CssColor::LightDark(..) => return Err(()),
CssColor::System(..) => return Err(())
})
}
}
Expand All @@ -2895,6 +2905,7 @@ macro_rules! color_space {
CssColor::Float(float) => (*float).into(),
CssColor::CurrentColor => return Err(()),
CssColor::LightDark(..) => return Err(()),
CssColor::System(..) => return Err(())
})
}
}
Expand Down Expand Up @@ -3578,3 +3589,106 @@ impl<'i, V: ?Sized + Visitor<'i, T>, T: Visit<'i, T, V>> Visit<'i, T, V> for RGB
Ok(())
}
}

enum_property! {
/// A CSS [system color](https://drafts.csswg.org/css-color/#css-system-colors) keyword.
pub enum SystemColor {
/// Background of accented user interface controls.
AccentColor,
/// Text of accented user interface controls.
AccentColorText,
/// Text in active links. For light backgrounds, traditionally red.
ActiveText,
/// The base border color for push buttons.
ButtonBorder,
/// The face background color for push buttons.
ButtonFace,
/// Text on push buttons.
ButtonText,
/// Background of application content or documents.
Canvas,
/// Text in application content or documents.
CanvasText,
/// Background of input fields.
Field,
/// Text in input fields.
FieldText,
/// Disabled text. (Often, but not necessarily, gray.)
GrayText,
/// Background of selected text, for example from ::selection.
Highlight,
/// Text of selected text.
HighlightText,
/// Text in non-active, non-visited links. For light backgrounds, traditionally blue.
LinkText,
/// Background of text that has been specially marked (such as by the HTML mark element).
Mark,
/// Text that has been specially marked (such as by the HTML mark element).
MarkText,
/// Background of selected items, for example a selected checkbox.
SelectedItem,
/// Text of selected items.
SelectedItemText,
/// Text in visited links. For light backgrounds, traditionally purple.
VisitedText,

// Deprecated colors: https://drafts.csswg.org/css-color/#deprecated-system-colors

/// Active window border. Same as ButtonBorder.
ActiveBorder,
/// Active window caption. Same as Canvas.
ActiveCaption,
/// Background color of multiple document interface. Same as Canvas.
AppWorkspace,
/// Desktop background. Same as Canvas.
Background,
/// The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border. Same as ButtonFace.
ButtonHighlight,
/// The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border. Same as ButtonFace.
ButtonShadow,
/// Text in caption, size box, and scrollbar arrow box. Same as CanvasText.
CaptionText,
/// Inactive window border. Same as ButtonBorder.
InactiveBorder,
/// Inactive window caption. Same as Canvas.
InactiveCaption,
/// Color of text in an inactive caption. Same as GrayText.
InactiveCaptionText,
/// Background color for tooltip controls. Same as Canvas.
InfoBackground,
/// Text color for tooltip controls. Same as CanvasText.
InfoText,
/// Menu background. Same as Canvas.
Menu,
/// Text in menus. Same as CanvasText.
MenuText,
/// Scroll bar gray area. Same as Canvas.
Scrollbar,
/// The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border. Same as ButtonBorder.
ThreeDDarkShadow,
/// The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border. Same as ButtonFace.
ThreeDFace,
/// The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border. Same as ButtonBorder.
ThreeDHighlight,
/// The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border. Same as ButtonBorder.
ThreeDLightShadow,
/// The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border. Same as ButtonBorder.
ThreeDShadow,
/// Window background. Same as Canvas.
Window,
/// Window frame. Same as ButtonBorder.
WindowFrame,
/// Text in windows. Same as CanvasText.
WindowText,
}
}

impl IsCompatible for SystemColor {
fn is_compatible(&self, browsers: Browsers) -> bool {
use SystemColor::*;
match self {
AccentColor | AccentColorText => Feature::AccentSystemColor.is_compatible(browsers),
_ => true
}
}
}

1 comment on commit 2983614

@toastal
Copy link
Contributor

@toastal toastal commented on 2983614 Mar 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to #685 ?

Please sign in to comment.