diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
commit | da4c7e7ed675c3bf405668739c3012d140856109 (patch) | |
tree | cdd868dba063fecba609a1d819de271f0d51b23e /toolkit/themes | |
parent | Adding upstream version 125.0.3. (diff) | |
download | firefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip |
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/themes')
50 files changed, 5925 insertions, 1000 deletions
diff --git a/toolkit/themes/linux/global/toolbar.css b/toolkit/themes/linux/global/toolbar.css index 05fc5eec49..9f4550a790 100644 --- a/toolkit/themes/linux/global/toolbar.css +++ b/toolkit/themes/linux/global/toolbar.css @@ -8,11 +8,6 @@ @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); -toolbox { - appearance: auto; - -moz-default-appearance: toolbox; -} - toolbar { background-color: -moz-headerbar; color: -moz-headerbartext; diff --git a/toolkit/themes/moz.build b/toolkit/themes/moz.build index ac0a9798bb..fe4ffb6949 100644 --- a/toolkit/themes/moz.build +++ b/toolkit/themes/moz.build @@ -29,3 +29,5 @@ else: with Files("**"): BUG_COMPONENT = ("Toolkit", "Themes") + +SPHINX_TREES["shared/design-system/docs"] = "shared/design-system/docs" diff --git a/toolkit/themes/osx/global/jar.mn b/toolkit/themes/osx/global/jar.mn index 19bf28220d..6d7b1975b5 100644 --- a/toolkit/themes/osx/global/jar.mn +++ b/toolkit/themes/osx/global/jar.mn @@ -12,7 +12,6 @@ toolkit.jar: skin/classic/global/menu.css skin/classic/global/menulist.css skin/classic/global/richlistbox.css - skin/classic/global/tabprompts.css skin/classic/global/tabbox.css skin/classic/global/toolbar.css skin/classic/global/wizard.css diff --git a/toolkit/themes/osx/global/tabprompts.css b/toolkit/themes/osx/global/tabprompts.css deleted file mode 100644 index 7259cf1829..0000000000 --- a/toolkit/themes/osx/global/tabprompts.css +++ /dev/null @@ -1,62 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Tab Modal Prompt boxes */ -.tabModalBackground, -tabmodalprompt { - background-color: hsla(0,0%,10%,.5); -} - -tabmodalprompt { - font-family: sans-serif; /* use content font not system UI font */ - font-size: 110%; -} - -.tabmodalprompt-mainContainer { - color: black; - background-color: hsla(0,0%,100%,.95); - background-clip: padding-box; - border-radius: 2px; - border: 1px solid hsla(0,0%,0%,.5); -} - -.tabmodalprompt-buttonContainer { - background-color: hsla(0,0%,0%,.05); - border-top: 1px solid hsla(0,0%,0%,.05); -} - -.tabmodalprompt-buttonContainer > button { - appearance: none; - padding: 2px 0; - margin: 0; - margin-inline-start: 8px; - border-radius: 2px; - color: black !important; - background-color: hsl(0,0%,90%); - background-image: linear-gradient(hsla(0,0%,100%,.7), transparent); - background-clip: padding-box; - border: 1px solid; - border-color: hsl(0,0%,65%) hsl(0,0%,60%) hsl(0,0%,50%); - box-shadow: 0 1px 0 hsla(0,0%,100%,.9) inset, - 0 1px 2px hsla(0,0%,0%,.1); -} - -.tabmodalprompt-buttonContainer > button[default=true] { - background-color: hsl(0,0%,79%); -} - -.tabmodalprompt-buttonContainer > button:hover { - background-color: hsl(0,0%,96%); -} - -.tabmodalprompt-buttonContainer > button:hover:active { - background-image: linear-gradient(hsla(0,0%,100%,.2), transparent); - background-color: hsl(0,0%,70%); - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, - 0 1px 3px hsla(0,0%,0%,.2); -} - -.tabmodalprompt-buttonContainer > button:focus-visible { - outline: var(--focus-outline); -} diff --git a/toolkit/themes/osx/global/toolbar.css b/toolkit/themes/osx/global/toolbar.css index 0a60ae57d9..4148b1e934 100644 --- a/toolkit/themes/osx/global/toolbar.css +++ b/toolkit/themes/osx/global/toolbar.css @@ -4,22 +4,9 @@ @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); -toolbox { - /* Setting -moz-default-appearance:toolbox causes sheets to attach under the - * toolbox and has no other effects. It doesn't render anything. */ - appearance: auto; - -moz-default-appearance: toolbox; -} - toolbar { min-width: 1px; min-height: 20px; - appearance: auto; - -moz-default-appearance: toolbar; -} - -toolbar:-moz-lwtheme { - appearance: none; } toolbarseparator { diff --git a/toolkit/themes/shared/aboutReader.css b/toolkit/themes/shared/aboutReader.css index 6e4206e293..764dd39702 100644 --- a/toolkit/themes/shared/aboutReader.css +++ b/toolkit/themes/shared/aboutReader.css @@ -39,7 +39,6 @@ body { --tooltip-border: transparent; --popup-background: #fff; --popup-border: rgba(0, 0, 0, 0.12); - --opaque-popup-border: rgb(224, 224, 224); --popup-line: var(--grey-30); --popup-shadow: rgba(49, 49, 49, 0.3); --popup-button-background: rgba(207, 207, 216, 0.33); @@ -47,9 +46,8 @@ body { --popup-button-background-hover: var(--toolbar-button-background-hover); --popup-button-foreground-hover: var(--main-foreground); --popup-button-background-active: var(--toolbar-button-background-active); - --popup-button-border: var(--popup-border); + --popup-button-border: rgba(0, 0, 0, 0.2); --selected-background: rgba(0, 97, 224, 0.3); - --selected-border: var(--primary-color); --outline-focus-color: var(--primary-color); --font-value-background: rgb(240, 240, 244); --font-value-border: var(--grey-30); @@ -61,6 +59,11 @@ body { --link-selected-background: var(--selected-background); --link-selected-foreground: #333; --visited-link-foreground: #b5007f; + --custom-theme-background: var(--color-white); + --custom-theme-foreground: #14151A; + --custom-theme-unvisited-links: var(--color-blue-50); + --custom-theme-visited-links: #321C64; + --custom-theme-selection-highlight: #FFFFCC; /* light colours */ } @@ -72,26 +75,62 @@ body.sepia { --icon-disabled-fill: rgba(91, 70, 54, 0.4); } -body.dark { - --main-background: var(--dark-theme-background); - --main-foreground: var(--dark-theme-foreground); - --primary-color: rgb(0, 221, 255); - --toolbar-border: rgba(249, 249, 250, 0.2); +body.gray { + --main-background: var(--grey-30); + --main-foreground: var(--light-theme-foreground); + --toolbar-border: var(--main-foreground); + --icon-fill: var(--main-foreground); +} + +body.dark, +body.contrast { --toolbar-box-shadow: black; --toolbar-button-background-hover: rgb(82, 82, 94); --toolbar-button-background-active: rgb(91, 91, 102); --popup-background: rgb(66, 65, 77); - --opaque-popup-border: #434146; + --popup-button-border: rgba(255, 255, 255, 0.12); --popup-line: rgba(249, 249, 250, 0.1); --popup-button-background: rgb(43, 42, 51); - --selected-background: rgba(0, 221, 255, 0.3); --font-value-background: rgba(249, 249, 250, 0.15); --font-value-border: #656468; - --icon-fill: rgb(251, 251, 254); --icon-disabled-fill: rgba(251, 251, 254, 0.4); --link-selected-foreground: #fff; --visited-link-foreground: #e675fd; + --selected-background: rgba(0, 221, 255, 0.3); /* dark colours */ + + .moz-reader-block-img { + filter: brightness(0.8) contrast(1.2); + } +} + +body.dark { + --main-background: var(--dark-theme-background); + --main-foreground: var(--dark-theme-foreground); + --primary-color: rgb(0, 221, 255); + --toolbar-border: rgba(249, 249, 250, 0.2); + --icon-fill: rgb(251, 251, 254); +} + +body.contrast { + --main-background: #000000; + --main-foreground: #fff; + --primary-color: #D6B4FD; + --toolbar-border: #FFEE32; + --icon-fill: #FFEE32; +} + +body.custom { + --main-background: var(--custom-theme-background); + --main-foreground: var(--custom-theme-foreground); + --link-foreground: var(--custom-theme-unvisited-links); + --visited-link-foreground: var(--custom-theme-visited-links); + --popup-button-foreground: var(--light-theme-foreground); + --popup-button-foreground-hover: var(--light-theme-foreground); + --tooltip-foreground: var(--light-theme-foreground); + --toolbar-border: var(--main-foreground); + --icon-fill: var(--main-foreground); + --icon-disabled-fill: rgba(91, 91, 102, 0.4); } body.hcm { @@ -116,7 +155,6 @@ body.hcm { --tooltip-border: CanvasText; --popup-background: Canvas; --popup-border: CanvasText; - --opaque-popup-border: CanvasText; --popup-line: CanvasText; --popup-button-background: ButtonFace; --popup-button-foreground: ButtonText; @@ -138,6 +176,11 @@ body.hcm { --visited-link-foreground: VisitedText; } +body.hcm .colors-dropdown { + /* Hide entire colors menu when HCM is on. */ + display: none; +} + body { margin: 0; padding: var(--body-padding); @@ -182,31 +225,29 @@ blockquote { border-inline-start: 2px solid var(--main-foreground) !important; } -.light-button, -.auto-button { - color: var(--light-theme-foreground); - background-color: var(--light-theme-background); -} - -@media (prefers-color-scheme: dark) { +.color-scheme-buttons { + .light-button, .auto-button { - color: var(--dark-theme-foreground); - background-color: var(--dark-theme-background); + color: var(--light-theme-foreground); + background-color: var(--light-theme-background); } - .moz-reader-block-img { - filter: brightness(0.8) contrast(1.2); + @media (prefers-color-scheme: dark) { + .auto-button { + color: var(--dark-theme-foreground); + background-color: var(--dark-theme-background); + } } -} -.dark-button { - color: var(--dark-theme-foreground); - background-color: var(--dark-theme-background); -} + .dark-button { + color: var(--dark-theme-foreground); + background-color: var(--dark-theme-background); + } -.sepia-button { - color: #5b4636; - background-color: #f4ecd8; + .sepia-button { + color: #5b4636; + background-color: #f4ecd8; + } } /* Loading/error message */ @@ -311,7 +352,7 @@ blockquote { */ margin-inline-start: max(calc(50% - 17px - var(--inline-padding)), calc(100% - 96px - 34px - 2 * var(--inline-padding))); - font-family: Helvetica, Arial, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Ubuntu, "Helvetica Neue", sans-serif; list-style: none; user-select: none; @@ -452,40 +493,26 @@ button:disabled { .dropdown .dropdown-popup { text-align: start; position: absolute; - inset-inline-start: 40px; + inset-inline-start: 32px; z-index: 1000; background-color: var(--popup-background); visibility: hidden; border-radius: 4px; border: 1px solid var(--popup-border); box-shadow: 0 0 10px 0 var(--popup-shadow); - top: 0; -} - -.open > .dropdown-popup { - visibility: visible; + top: var(--space-xsmall); } -.dropdown-arrow { - position: absolute; - height: 24px; - width: 16px; - inset-inline-start: -16px; - background-image: url("chrome://global/skin/reader/RM-Type-Controls-Arrow.svg"); - display: block; - -moz-context-properties: fill, stroke; - fill: var(--popup-background); - stroke: var(--opaque-popup-border); - pointer-events: none; -} - -.dropdown-arrow:dir(rtl) { - transform: scaleX(-1); +.dropdown-popup h2 { + font-size: var(--font-size-root); + font-weight: var(--font-weight-bold); + color: var(--popup-button-foreground); + margin-block: var(--space-large); + margin-inline: var(--space-large) 0; } -/* Align the style dropdown arrow (narrate does its own) */ -.style-dropdown .dropdown-arrow { - top: 7px; +.open > .dropdown-popup { + visibility: visible; } /* Font style popup */ @@ -513,13 +540,14 @@ button:disabled { box-sizing: border-box; width: 36px; height: 20px; - line-height: 20px; + line-height: 18px; display: flex; justify-content: center; align-content: center; margin: auto; border-radius: 10px; border: 1px solid var(--font-value-border); + color: var(--popup-button-foreground); background-color: var(--font-value-background); } @@ -547,16 +575,11 @@ button:disabled { outline-offset: -2px; } -.radiorow:not(:last-child), -.buttonrow:not(:last-child) { +.style-dropdown .radiorow:not(:last-child), +.style-dropdown .buttonrow:not(:last-child) { border-bottom: 1px solid var(--popup-line); } -body.hcm .buttonrow.line-height-buttons { - /* On HCM the .color-scheme-buttons row is hidden, so remove the border from the row above it */ - border-bottom: none; -} - .radiorow > label { position: relative; box-sizing: border-box; @@ -565,27 +588,16 @@ body.hcm .buttonrow.line-height-buttons { } .radiorow > label[checked] { - border-color: var(--selected-border); + border: 2px solid var(--link-selected-foreground); } -/* For the hover style, we draw a line under the item by means of a - * pseudo-element. Because these items are variable height, and - * because their contents are variable height, position it absolutely, - * and give it a width of 100% (the content width) + 4px for the 2 * 2px - * border width. - */ -.radiorow > input[type=radio]:focus-visible + label::after, -.radiorow > label:hover::after { - content: ""; - display: block; - border-bottom: 2px solid var(--selected-border); - border-radius: 4px; - width: calc(100% + 4px); - position: absolute; - /* to skip the 2 * 2px border + 2px spacing. */ - bottom: -6px; - /* Match the start of the 2px border of the element: */ - inset-inline-start: -2px; +.radiorow > label:hover { + background-color: var(--toolbar-button-background-hover); +} + +.radiorow > input[type=radio]:focus-visible + label { + outline: 2px solid var(--primary-color); + outline-offset: var(--focus-outline-offset); } .font-type-buttons > label { @@ -608,6 +620,16 @@ body.hcm .buttonrow.line-height-buttons { padding-top: 2px; } +.font-type-buttons { + > label:first-of-type { + margin-inline-start: var(--space-large); + } + + > label:last-of-type { + margin-inline-end: var(--space-large); + } +} + .font-type-buttons > label[checked] { background-color: var(--selected-background); } @@ -642,6 +664,7 @@ body.hcm .color-scheme-buttons { justify-content: center; /* We want 10px between items, but there's no margin collapsing in flexbox. */ margin: 10px 5px; + border-color: var(--popup-border); } .color-scheme-buttons > input:first-child + label { @@ -652,6 +675,108 @@ body.hcm .color-scheme-buttons { margin-inline-end: 10px; } +/* Separate colors menu popup */ + +#color-controls { + padding-block-end: var(--space-large); + width: 400px; +} + +button-group { + display: flex; + font-size: var(--font-size-small); +} + +button[is="named-deck-button"] { + background: none; + color: var(--popup-button-foreground); + border: 1px var(--popup-button-border); + border-style: solid none; + padding: var(--space-small); + flex-basis: 50%; + + &[selected] { + color: var(--primary-color); + border-top: 2px solid var(--primary-color); + } +} + +.custom-colors-selection { + display: flex; + flex-direction: column; + gap: var(--space-small); + padding: var(--space-large); + list-style-type: none; + font-size: var(--font-size-root); + color: var(--popup-button-foreground); +} + +.colors-menu-color-scheme-buttons { + flex-wrap: wrap; + margin-block-start: var(--space-small); +} + +.colors-menu-color-scheme-buttons > label { + height: 48px; + width: calc(50% - 21px); + font-size: var(--font-size-root); + color: var(--popup-button-foreground); + border: 1px solid var(--popup-button-border); + border-radius: var(--border-radius-small); + /* Center content vertically and justify left horizontally */ + display: inline-flex; + align-items: center; + justify-content: start; + margin: var(--space-xsmall); +} + +.colors-menu-color-scheme-buttons > label:before { + content: ""; + display: inline-block; + width: 24px; + height: 24px; + border-radius: var(--border-radius-circle); + outline: 1px solid var(--popup-button-border); + margin: 0 10px 0 12px; +} + +.colors-menu-color-scheme-buttons { + .auto-button:before { + background: linear-gradient(to right, var(--light-theme-background) 50%, var(--dark-theme-background) 50%);; + } + + .light-button:before { + background-color: var(--light-theme-background); + } + + .dark-button:before { + background-color: var(--dark-theme-background); + } + + .sepia-button:before { + background-color: #f4ecd8; + } + + .contrast-button:before { + background-color: #000000; + } + + .gray-button:before { + background-color: var(--grey-30); + } +} + +.custom-colors-reset-button { + display: block; + background: none; + border: none; + padding: 0 var(--space-large); + color: var(--primary-color); + text-decoration: underline; + font-size: var(--font-size-root); + cursor: pointer; +} + /* Toolbar icons */ .close-button { @@ -662,6 +787,10 @@ body.hcm .color-scheme-buttons { background-image: url("chrome://global/skin/reader/RM-Type-Controls-24x24.svg"); } +.colors-button { + background-image: url("chrome://mozapps/skin/extensions/category-themes.svg"); +} + .minus-button { background-size: 18px 18px; background-image: url("chrome://global/skin/reader/RM-Minus-24x24.svg"); diff --git a/toolkit/themes/shared/design-system/design-tokens.json b/toolkit/themes/shared/design-system/design-tokens.json new file mode 100644 index 0000000000..c807335942 --- /dev/null +++ b/toolkit/themes/shared/design-system/design-tokens.json @@ -0,0 +1,944 @@ +{ + "attention": { + "dot": { + "color": { + "value": { + "platform": { + "default": "AccentColor" + }, + "brand": { + "light": "#2ac3a2", + "dark": "#54ffbd" + }, + "prefersContrast": "AccentColor" + } + } + } + }, + "background": { + "color": { + "box": { + "value": { + "light": "{color.white}", + "dark": "{color.gray.80}", + "prefersContrast": "{background.color.canvas}" + } + }, + "canvas": { + "value": { + "prefersContrast": "Canvas", + "brand": { + "light": "{color.white}", + "dark": "{color.gray.90}" + }, + "platform": { + "default": "Canvas" + } + } + }, + "critical": { + "value": { + "light": "{color.red.05}", + "dark": "{color.red.80}", + "prefersContrast": "{background.color.canvas}" + } + }, + "information": { + "value": { + "light": "{color.blue.05}", + "dark": "{color.blue.80}", + "prefersContrast": "{background.color.canvas}" + } + }, + "success": { + "value": { + "light": "{color.green.05}", + "dark": "{color.green.80}", + "prefersContrast": "{background.color.canvas}" + } + }, + "warning": { + "value": { + "light": "{color.yellow.05}", + "dark": "{color.yellow.80}", + "prefersContrast": "{background.color.canvas}" + } + } + } + }, + "border": { + "color": { + "@base": { + "value": { + "prefersContrast": "{text.color.@base}" + } + }, + "interactive": { + "@base": { + "value": { + "prefersContrast": "{text.color.@base}", + "forcedColors": "ButtonText", + "brand": { + "light": "{color.gray.60}", + "dark": "{color.gray.50}" + }, + "platform": { + "default": "color-mix(in srgb, currentColor 15%, {color.gray.60})" + } + } + }, + "hover": { + "value": { + "default": "{border.color.interactive.@base}", + "forcedColors": "SelectedItem" + } + }, + "active": { + "value": { + "default": "{border.color.interactive.@base}", + "forcedColors": "ButtonText" + } + }, + "disabled": { + "value": { + "default": "{border.color.interactive.@base}", + "forcedColors": "GrayText" + } + } + } + }, + "radius": { + "circle": { + "value": "9999px" + }, + "small": { + "value": "4px" + }, + "medium": { + "value": "8px" + } + }, + "width": { + "value": "1px" + } + }, + "box": { + "shadow": { + "10": { + "value": "0 1px 4px {color.black.a10}" + } + } + }, + "button": { + "background": { + "color": { + "@base": { + "comment": "TODO Bug 1821203 - Gray use needs to be consolidated", + "value": { + "forcedColors": "ButtonFace", + "brand": { + "default": "color-mix(in srgb, currentColor 7%, transparent)" + }, + "platform": { + "default": "var(--button-bgcolor)" + } + } + }, + "hover": { + "value": { + "forcedColors": "SelectedItemText", + "brand": { + "default": "color-mix(in srgb, currentColor 14%, transparent)" + }, + "platform": { + "default": "var(--button-hover-bgcolor)" + } + } + }, + "active": { + "value": { + "forcedColors": "SelectedItemText", + "brand": { + "default": "color-mix(in srgb, currentColor 21%, transparent)" + }, + "platform": { + "default": "var(--button-active-bgcolor)" + } + } + }, + "disabled": { + "value": { + "default": "{button.background.color.@base}", + "forcedColors": "ButtonFace" + } + }, + "primary": { + "@base": { + "value": "{color.accent.primary.@base}" + }, + "hover": { + "value": "{color.accent.primary.hover}" + }, + "active": { + "value": "{color.accent.primary.active}" + }, + "disabled": { + "value": { + "default": "{button.background.color.primary.@base}", + "forcedColors": "{button.text.color.disabled}" + } + } + }, + "destructive": { + "@base": { + "value": { + "light": "{color.red.50}", + "dark": "{color.red.30}", + "forcedColors": "{button.background.color.primary.@base}" + } + }, + "active": { + "value": { + "light": "{color.red.70}", + "dark": "{color.red.05}", + "forcedColors": "{button.background.color.primary.active}" + } + }, + "disabled": { + "value": { + "default": "{button.background.color.destructive.@base}", + "forcedColors": "{button.background.color.primary.disabled}" + } + }, + "hover": { + "value": { + "light": "{color.red.60}", + "dark": "{color.red.10}", + "forcedColors": "{button.background.color.primary.hover}" + } + } + }, + "ghost": { + "@base": { + "value": { + "default": "transparent", + "prefersContrast": "{button.background.color.@base}", + "forcedColors": "{button.background.color.@base}" + } + }, + "active": { + "value": "{button.background.color.active}" + }, + "disabled": { + "value": { + "default": "{button.background.color.ghost.@base}", + "forcedColors": "{button.background.color.disabled}" + } + }, + "hover": { + "value": "{button.background.color.hover}" + } + } + } + }, + "border": { + "@base": { + "value": "{border.width} solid {button.border.color.@base}" + }, + "color": { + "@base": { + "value": { + "default": "transparent", + "prefersContrast": "{button.text.color.@base}", + "forcedColors": "{border.color.interactive.@base}" + } + }, + "active": { + "value": { + "default": "{button.border.color.@base}", + "forcedColors": "{border.color.interactive.active}" + } + }, + "destructive": { + "@base": { + "value": { + "default": "transparent", + "forcedColors": "{button.border.color.primary.@base}" + } + }, + "active": { + "value": { + "default": "{button.border.color.destructive.@base}", + "forcedColors": "{button.border.color.primary.active}" + } + }, + "disabled": { + "value": { + "default": "{button.border.color.destructive.@base}", + "forcedColors": "{button.border.color.primary.disabled}" + } + }, + "hover": { + "value": { + "default": "{button.border.color.destructive.@base}", + "forcedColors": "{button.border.color.primary.hover}" + } + } + }, + "disabled": { + "value": { + "default": "{button.border.color.@base}", + "forcedColors": "{border.color.interactive.disabled}" + } + }, + "ghost": { + "@base": { + "value": { + "default": "{button.border.color.@base}" + } + }, + "active": { + "value": { + "default": "{button.border.color.active}" + } + }, + "disabled": { + "value": { + "default": "{button.border.color.disabled}" + } + }, + "hover": { + "value": { + "default": "{button.border.color.hover}" + } + } + }, + "hover": { + "value": { + "default": "{button.border.color.@base}", + "forcedColors": "{border.color.interactive.hover}" + } + }, + "primary": { + "@base": { + "value": { + "default": "transparent", + "forcedColors": "ButtonFace" + } + }, + "active": { + "value": { + "default": "{button.border.color.primary.@base}", + "forcedColors": "ButtonText" + } + }, + "disabled": { + "value": "{button.border.color.primary.@base}" + }, + "hover": { + "value": { + "default": "{button.border.color.primary.@base}", + "forcedColors": "SelectedItemText" + } + } + } + }, + "radius": { + "value": "{border.radius.small}" + } + }, + "font": { + "size": { + "@base": { + "value": "{font.size.root}" + }, + "small": { + "value": "{font.size.small}" + } + }, + "weight": { + "value": "{font.weight.bold}" + } + }, + "min": { + "height": { + "@base": { + "value": "{size.item.large}" + }, + "small": { + "value": "{size.item.medium}" + } + } + }, + "opacity": { + "disabled": { + "value": { + "default": 0.5, + "forcedColors": 1 + } + } + }, + "padding": { + "@base": { + "value": "{space.xsmall} {space.large}" + }, + "icon": { + "value": 0 + } + }, + "size": { + "icon": { + "@base": { + "value": "{button.min.height.@base}" + }, + "small": { + "value": "{button.min.height.small}" + } + } + }, + "text": { + "color": { + "@base": { + "value": { + "forcedColors": "ButtonText", + "brand": { + "light": "{color.gray.100}", + "dark": "{color.gray.05}" + }, + "platform": { + "default": "var(--button-color)" + } + } + }, + "active": { + "value": { + "default": "{button.text.color.@base}", + "forcedColors": "SelectedItem" + } + }, + "destructive": { + "@base": { + "value": { + "light": "{color.gray.05}", + "dark": "{color.gray.100}", + "forcedColors": "{button.text.color.primary.@base}" + } + }, + "active": { + "value": { + "default": "{button.text.color.destructive.@base}", + "forcedColors": "{button.text.color.primary.active}" + } + }, + "disabled": { + "value": { + "default": "{button.text.color.destructive.@base}", + "forcedColors": "{button.text.color.primary.disabled}" + } + }, + "hover": { + "value": { + "default": "{button.text.color.destructive.@base}", + "forcedColors": "{button.text.color.primary.hover}" + } + } + }, + "disabled": { + "value": { + "default": "{button.text.color.@base}", + "forcedColors": "GrayText" + } + }, + "ghost": { + "@base": { + "value": { + "default": "{button.text.color.@base}" + } + }, + "active": { + "value": { + "default": "{button.text.color.active}" + } + }, + "disabled": { + "value": { + "default": "{button.text.color.disabled}" + } + }, + "hover": { + "value": { + "default": "{button.text.color.hover}" + } + } + }, + "hover": { + "value": { + "default": "{button.text.color.@base}", + "forcedColors": "SelectedItem" + } + }, + "primary": { + "@base": { + "value": { + "forcedColors": "ButtonFace", + "brand": { + "light": "{color.gray.05}", + "dark": "{color.gray.100}" + }, + "platform": { + "default": "var(--button-primary-color)" + } + } + }, + "active": { + "value": "{button.text.color.primary.hover}" + }, + "disabled": { + "value": "{button.text.color.primary.@base}" + }, + "hover": { + "value": { + "default": "{button.text.color.primary.@base}", + "forcedColors": "SelectedItemText" + } + } + } + } + } + }, + "checkbox": { + "margin": { + "inline": { + "value": "{space.small}" + } + }, + "size": { + "comment": "TODO Bug 1876537: Make this em-based, probably?", + "value": "{size.item.small}" + } + }, + "color": { + "black": { + "a10": { + "value": "rgba(0, 0, 0, 0.1)" + } + }, + "blue": { + "05": { + "value": "#deeafc" + }, + "30": { + "value": "#73a7f3" + }, + "50": { + "value": "#0060df" + }, + "60": { + "value": "#0250bb" + }, + "70": { + "value": "#054096" + }, + "80": { + "value": "#003070" + } + }, + "cyan": { + "20": { + "value": "#aaf2ff" + }, + "30": { + "value": "#80ebff" + }, + "50": { + "value": "#00ddff" + } + }, + "gray": { + "05": { + "value": "#fbfbfe" + }, + "50": { + "value": "#bfbfc9" + }, + "60": { + "value": "#8f8f9d" + }, + "70": { + "value": "#5b5b66" + }, + "80": { + "value": "#23222b" + }, + "90": { + "value": "#1c1b22" + }, + "100": { + "value": "#15141a" + } + }, + "green": { + "05": { + "value": "#d8eedc" + }, + "30": { + "value": "#4dbc87" + }, + "50": { + "value": "#017a40" + }, + "80": { + "value": "#004725" + } + }, + "red": { + "05": { + "value": "#ffe8e8" + }, + "10": { + "value": "#ffbdc5" + }, + "20": { + "value": "#ff9aa2" + }, + "30": { + "value": "#f37f98" + }, + "50": { + "value": "#d7264c" + }, + "60": { + "value": "#ac1e3d" + }, + "70": { + "value": "#8a1831" + }, + "80": { + "value": "#690f22" + } + }, + "yellow": { + "05": { + "value": "#ffebcd" + }, + "30": { + "value": "#e49c49" + }, + "50": { + "value": "#cd411e" + }, + "80": { + "value": "#5a3100" + } + }, + "white": { + "value": "#ffffff" + }, + "accent": { + "primary": { + "@base": { + "value": { + "forcedColors": "ButtonText", + "brand": { + "light": "{color.blue.50}", + "dark": "{color.cyan.50}" + }, + "platform": { + "default": "var(--button-primary-bgcolor, AccentColor)" + } + } + }, + "hover": { + "value": { + "forcedColors": "SelectedItem", + "brand": { + "light": "{color.blue.60}", + "dark": "{color.cyan.30}" + }, + "platform": { + "default": "var(--button-primary-hover-bgcolor)" + } + } + }, + "active": { + "value": { + "forcedColors": "{color.accent.primary.hover}", + "brand": { + "light": "{color.blue.70}", + "dark": "{color.cyan.20}" + }, + "platform": { + "default": "var(--button-primary-active-bgcolor)" + } + } + } + } + } + }, + "focus": { + "outline": { + "@base": { + "value": "{focus.outline.width} solid {focus.outline.color}" + }, + "color": { + "value": "{color.accent.primary.@base}" + }, + "inset": { + "value": "calc(-1 * {focus.outline.width})" + }, + "offset": { + "value": "2px" + }, + "width": { + "value": "2px" + } + } + }, + "font": { + "size": { + "root": { + "value": { + "brand": { + "default": "15px" + }, + "platform": { + "default": "unset" + } + } + }, + "small": { + "value": { + "brand": { + "default": "0.867rem" + }, + "platform": { + "default": "unset" + } + } + }, + "large": { + "value": { + "brand": { + "default": "1.133rem" + }, + "platform": { + "default": "unset" + } + } + }, + "xlarge": { + "value": { + "brand": { + "default": "1.467rem" + }, + "platform": { + "default": "unset" + } + } + }, + "xxlarge": { + "value": { + "brand": { + "default": "1.6rem" + }, + "platform": { + "default": "unset" + } + } + } + }, + "weight": { + "@base": { + "value": "normal" + }, + "bold": { + "value": 600 + } + } + }, + "icon": { + "color": { + "@base": { + "value": { + "light": "{color.gray.70}", + "dark": "{color.gray.05}", + "prefersContrast": "{text.color.@base}" + } + }, + "information": { + "value": { + "light": "{color.blue.50}", + "dark": "{color.blue.30}", + "prefersContrast": "{icon.color.@base}" + } + }, + "success": { + "value": { + "light": "{color.green.50}", + "dark": "{color.green.30}", + "prefersContrast": "{icon.color.@base}" + } + }, + "warning": { + "value": { + "light": "{color.yellow.50}", + "dark": "{color.yellow.30}", + "prefersContrast": "{icon.color.@base}" + } + }, + "critical": { + "value": { + "light": "{color.red.50}", + "dark": "{color.red.30}", + "prefersContrast": "{icon.color.@base}" + } + } + }, + "size": { + "default": { + "value": "{size.item.small}" + } + } + }, + "input": { + "text": { + "min": { + "height": { + "value": "{button.min.height.@base}" + } + } + } + }, + "link": { + "color": { + "@base": { + "value": { + "prefersContrast": "LinkText", + "brand": { + "default": "{color.accent.primary.@base}" + }, + "platform": { + "default": "LinkText" + } + } + }, + "hover": { + "value": { + "prefersContrast": "LinkText", + "brand": { + "default": "{color.accent.primary.hover}" + }, + "platform": { + "default": "LinkText" + } + } + }, + "active": { + "value": { + "prefersContrast": "ActiveText", + "brand": { + "default": "{color.accent.primary.active}" + }, + "platform": { + "default": "ActiveText" + } + } + }, + "visited": { + "value": { + "prefersContrast": "{link.color.@base}", + "brand": { + "default": "{link.color.@base}" + }, + "platform": { + "default": "{link.color.@base}" + } + } + } + }, + "focus": { + "outline": { + "offset": { + "comment": "Not using --force-outline-offset for links because that's intended for\nelements with a background, and we only want a slight offset here while\nnot overlapping adjacent text", + "value": "1px" + } + } + } + }, + "outline": { + "color": { + "error": { + "value": { + "light": "{color.red.50}", + "dark": "{color.red.20}", + "prefersContrast": "{border.color.@base}" + } + } + } + }, + "size": { + "item": { + "small": { + "value": "16px" + }, + "medium": { + "value": "28px" + }, + "large": { + "value": "32px" + } + } + }, + "space": { + "xxsmall": { + "value": "calc(0.5 * {space.xsmall})" + }, + "xsmall": { + "value": "0.267rem" + }, + "small": { + "value": "calc(2 * {space.xsmall})" + }, + "medium": { + "value": "calc(3 * {space.xsmall})" + }, + "large": { + "value": "calc(4 * {space.xsmall})" + }, + "xlarge": { + "value": "calc(6 * {space.xsmall})" + }, + "xxlarge": { + "value": "calc(8 * {space.xsmall})" + } + }, + "text": { + "color": { + "@base": { + "value": { + "prefersContrast": "CanvasText", + "brand": { + "light": "{color.gray.100}", + "dark": "{color.gray.05}" + }, + "platform": { + "default": "currentColor" + } + } + }, + "deemphasized": { + "value": { + "default": "color-mix(in srgb, currentColor 69%, transparent)", + "prefersContrast": "inherit" + } + }, + "error": { + "value": { + "light": "{color.red.50}", + "dark": "{color.red.20}", + "prefersContrast": "inherit" + } + } + } + } +} diff --git a/toolkit/themes/shared/design-system/README.design-tokens.stories.md b/toolkit/themes/shared/design-system/docs/README.design-tokens.stories.md index 156ab35e19..52651c1d59 100644 --- a/toolkit/themes/shared/design-system/README.design-tokens.stories.md +++ b/toolkit/themes/shared/design-system/docs/README.design-tokens.stories.md @@ -41,173 +41,15 @@ _The following documentation borrows from Nathan Curtis' essay on [Naming Design Design tokens' variable names follow a taxonomy with distinct classification levels and sublevels, forming a prescriptive vocabulary of descriptive terms which are classified by category. -<div class="box container-width-large"> -<div class="box"> - <div class="box vertical"> - <b>Ecosystem</b> - <div class="post-it"><b>Domain</b></div> - </div> - <div class="box vertical"> - <b>Object</b> - <div class="box"> - <div class="post-it green"><b>Pattern</b></div> - <div class="post-it green"><b>Component</b></div> - <div class="post-it green"><b>Element</b></div> - </div> - </div> - <div class="box vertical"> - <b>Category</b> - <div class="box"> - <div class="post-it orange"><b>Type</b></div> - <div class="post-it orange"><b>Concept</b></div> - <div class="post-it orange"><b>Property</b></div> - </div> - </div> - <div class="box vertical"> - <b>Modifier</b> - <div class="box"> - <div class="post-it blue"><b>Variant</b></div> - <div class="post-it blue"><b>State</b></div> - <div class="post-it blue"><b>Scale</b></div> - </div> - </div> -</div> - -<div class="box"> - <div class="box vertical"> - <div class="post-it disabled"></div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it green disabled"></div> - <div class="post-it green disabled"></div> - <div class="post-it green disabled"></div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it orange">Color</div> - <div class="post-it orange disabled"></div> - <div class="post-it orange disabled"></div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it blue">Blue</div> - <div class="post-it blue disabled"></div> - <div class="post-it blue">50</div> - </div> - </div> -</div> - -<div class="box"> - <div class="box vertical"> - <div class="post-it disabled"></div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it green disabled"></div> - <div class="post-it green disabled"></div> - <div class="post-it green disabled"></div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it orange">Size</div> - <div class="post-it orange">Item</div> - <div class="post-it orange disabled"></div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it blue disabled"></div> - <div class="post-it blue disabled"></div> - <div class="post-it blue">Small</div> - </div> - </div> -</div> - -<div class="box"> - <div class="box vertical"> - <div class="post-it disabled"></div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it green disabled"></div> - <div class="post-it green">Link</div> - <div class="post-it green disabled"></div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it orange disabled"></div> - <div class="post-it orange disabled"></div> - <div class="post-it orange">Color</div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it blue disabled"></div> - <div class="post-it blue disabled"></div> - <div class="post-it blue disabled"></div> - </div> - </div> -</div> - -<div class="box"> - <div class="box vertical"> - <div class="post-it disabled"></div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it green disabled"></div> - <div class="post-it green">Link</div> - <div class="post-it green">Focus Outline</div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it orange disabled"></div> - <div class="post-it orange disabled"></div> - <div class="post-it orange">Color</div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it blue disabled"></div> - <div class="post-it blue disabled"></div> - <div class="post-it blue disabled"></div> - </div> - </div> -</div> - -<div class="box"> - <div class="box vertical"> - <div class="post-it">Shopping</div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it green disabled"></div> - <div class="post-it green">Card</div> - <div class="post-it green disabled"></div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it orange disabled"></div> - <div class="post-it orange disbaled"></div> - <div class="post-it orange">Border Color</div> - </div> - </div> - <div class="box vertical"> - <div class="box"> - <div class="post-it blue disabled"></div> - <div class="post-it blue disabled"></div> - <div class="post-it blue disabled"></div> - </div> - </div> -</div> -</div> +![ + Our design system taxonomy has distinct classifiction levels and sublevels. \ + There are four high level buckets: Ecosystem, Object, Category, and Modifier. \ + Within the Ecosystem bucket is one sublevel: Domain. \ + Within the Object bucket are three sublevels: Pattern, Component, and Element. \ + Within the Category bucket are three sublevels: Type, Concept, and Property. \ + Within the Modifier bucket are three sublevels: Variant, State, and Scale. \ + Each of these levels and sublevels are explained in further detail after this graphic. \ + ](./img/taxonomy-overview.png) ### Ecosystem The ecosystem level helps describe the context that a token is scoped to. @@ -219,17 +61,7 @@ For example, if a token is specific to a certain feature, you can use the domain Example from [browser/components/shopping/content/shopping-container.css](https://searchfox.org/mozilla-central/rev/02841791400cf7cf5760c0cfaf31f5d772624253/browser/components/shopping/content/shopping-container.css#7): -<div class="box"> - <div class="post-it big"> - <strong>shopping</strong> - </div> - <div class="post-it blue big"> - header - </div> - <div class="post-it blue big"> - font-size - </div> -</div> +![Example showing "shopping" as the 'Domain' part of the "shopping-header-font-size" token](./img/ecosystem-domain.png) ### Objects The object level helps define the object (or objects) that the token applies to. @@ -237,44 +69,17 @@ The object level helps define the object (or objects) that the token applies to. #### Pattern A design pattern that is composed of, or represents, multiple related components. -<div class="box"> - <div class="post-it big"> - <strong>input</strong> - </div> - <div class="post-it blue big"> - text - </div> - <div class="post-it blue big"> - min-height - </div> -</div> +![Example showing "input" as the 'Pattern' part of the "input-text-min-height" token](./img/objects-pattern-example.png) #### Component The component name. -<div class="box"> - <div class="post-it big"> - <strong>toggle</strong> - </div> - <div class="post-it blue big"> - background-color - </div> -</div> +![Example showing "toggle" as the 'Component' part of the "toggle-background-color" token](./img/component-name-example.png) #### Nested element Any element that may be nested within a component (e.g. an icon). -<div class="box"> - <div class="post-it blue big"> - message-bar - </div> - <div class="post-it big"> - <strong>icon</strong> - </div> - <div class="post-it blue big"> - color - </div> -</div> +![Example showing "icon" as the 'Nested Element' part of the "message-bar-icon-color" token](./img/nested-element-example.png) ### Categories The category level helps define the visual style that apply to the token. @@ -282,32 +87,12 @@ The category level helps define the visual style that apply to the token. #### Type The type of style category a design token belongs to. -<div class="box"> - <div class="post-it big"> - <strong>color</strong> - </div> - <div class="post-it blue big"> - blue - </div> - <div class="post-it blue big"> - 50 - </div> -</div> +![Example showing "color" as the 'Type' part of the "color-blue-50" token](./img/categories-type-example.png) #### Concept A concept further describes user interface styles. They are either industry-wide patterns, or they are terms determined by our team based on specific user interface style needs. For example, "accent" is a common design industry term used for deliniating a brand's or product's accent color(s) that we happen to use for our color tokens. -<div class="box"> - <div class="post-it blue big"> - color - </div> - <div class="post-it big"> - <strong>accent</strong> - </div> - <div class="post-it blue big"> - primary - </div> -</div> +![Example showing "accent" as the 'Concept" part of the "color-accent-primary" token](./img/categories-concept-example.png) To further illustrate this taxonomy level, here are detailed explanations and definitions of existing concepts: @@ -315,11 +100,11 @@ To further illustrate this taxonomy level, here are detailed explanations and de We use the "accent" color concept for referring to our brand and the operating system (platform) accent colors. The brand and platform accent colors are used as the primary color for accentuating and characterizing several Firefox UI elements' (e.g. buttons, focus outlines, links, icons, and more). ##### Interactive -We use the "interactive" concept to describe design tokens that pertain to interactive elements. For example, `--border-interactive-color` is used on [moz-toggle](https://searchfox.org/mozilla-central/rev/956e25260926097a4d54d5aeb0e06347841616bf/toolkit/content/widgets/moz-toggle/moz-toggle.css#40) since interactive elements such as toggles, radios, and checkboxes share the same border color pattern that is different from our default border color. +We use the "interactive" concept to describe design tokens that pertain to interactive elements. For example, `--border-color-interactive` is used on [moz-toggle](https://searchfox.org/mozilla-central/rev/956e25260926097a4d54d5aeb0e06347841616bf/toolkit/content/widgets/moz-toggle/moz-toggle.css#40) since interactive elements such as toggles, radios, and checkboxes share the same border color pattern that is different from our default border color. ```css /* moz-toggle.css */ ---toggle-border-color: var(--border-interactive-color); +--toggle-border-color: var(--border-color-interactive); ``` ##### Item @@ -334,14 +119,7 @@ We use the "item" concept as a modifier on top of the "size" type tokens group t #### Property A property (e.g. size, width, color, fill) further describes a design tokens' style, although this is not to be confused with the categorical type of token mentioned above, albeit they often use similar terms. Note that sometimes properties are double-worded, and that's totally fine (e.g. min-width, background-color) -<div class="box"> - <div class="post-it big"> - <strong>border-radius</strong> - </div> - <div class="post-it blue big"> - circle - </div> -</div> +![Example showing "border-radius" as the 'Property' part of the "border-radius-circle" token](./img/categories-property-example.png) ### Modifiers The modifier level helps further classify a design token's characteristic with further specification. @@ -349,61 +127,21 @@ The modifier level helps further classify a design token's characteristic with f #### Variant A variant specifies a token from a group of tokens related by a common meaning but that have varying purpose. -<div class="box vertical"> -<div class="box"> - <div class="post-it blue big"> - icon - </div> - <div class="post-it blue big"> - color - </div> - <div class="post-it big"> - <strong>success</strong> - </div> -</div> - -<div class="box"> - <div class="post-it blue big"> - icon - </div> - <div class="post-it blue big"> - color - </div> - <div class="post-it big"> - <strong>critical</strong> - </div> -</div> -</div> +![Two examples: + one showing "success" as the 'Variant' part of the "icon-color-success" token. \ + another showing "critical" as the 'Variant' part of the "icon-color-critical" token. \ +](./img/modifiers-variant-example.png) #### State A state defines possible intereactive states of a design token. (e.g. hover, active, focus, disabled) -<div class="box"> - <div class="post-it blue big"> - button - </div> - <div class="post-it blue big"> - background-color - </div> - <div class="post-it big"> - <strong>hover</strong> - </div> -</div> +![Example showing "hover" as the 'State' part of the "button-background-color-hover" token](./img/modifiers-state-example.png) #### Scale A scale defines a collection of tokens that relate to one another's but vary by their type, such as a collection of size units, or any other relationship that requires differentiating tokens by a determined scale. -<div class="box vertical"> -<div class="box"> - <div class="post-it blue big"> - font-size - </div> - <div class="post-it big"> - <strong>small</strong> - </div> -</div> -</div> +![Example showing "small" as the 'Scale' part of the "font-size-small" token](./img/modifiers-scale-example.png) Today we have scales based off a sequence of numbers or t-shirt sizing. @@ -442,52 +180,20 @@ You will see that the font size scale is missing what would be a logical "medium The `--font-size-root` token was created for specific use under the document's `:root` in order to set the default font size for our relative typography scale. We label our default font size token as `root` in order to be specific **and** intentional. -<div class="box"> - <div class="post-it blue big"> - font-size - </div> - <div class="post-it big"> - <strong>root</strong> - </div> -</div> +![Example showing "root" being the 'Scale' part of the "font-size-root" token](./img/modifiers-font-size-example.png) It's okay to include intentional terms within scales that better represent the meaning of a value and when to use it. For example, our border radius collection, which uses t-shirt sizing, also mixes the 'circle' option within its scale in order to describe a border radius that will create a circular effect: -<div class="box"> - <div class="post-it blue big"> - border-radius - </div> - <div class="post-it big"> - <strong>circle</strong> - </div> -</div> +![Example showing "circle" being the 'Scale' part of the "border-radius-circle" token](./img/modifiers-border-radius-example.png) ## Naming guidelines The goal of design tokens naming is modularity and legibility. -<div class="box container-width-large"> - <div class="box vertical"> - <b>Ecosystem</b> - Domain - <div class="post-it big green">brand</div> - </div> - <div class="box"> - <div class="box vertical"> - <b>Category</b> - <div class="box"> - <div class="box vertical"> - Type - <div class="post-it big orange">color</div> - </div> - <div class="box vertical"> - Concept - <div class="post-it big orange">accent</div> - </div> - </div> - </div> - </div> -</div> +![ + An example showing the "brand-color-accent" token. \ + "brand" is part of the 'Domain' ecosystem, while "color" and "accent" belong to 'Type' and 'Concept' sublevel of Category respectively. + ](./img/modifiers-naming-guidelines.png) Meanings and the relationship between meanings can be complex, therefore taxonomy levels are chained to provide clarity. (see example above) @@ -521,62 +227,13 @@ Shared tokens ([tokens-shared.css](https://searchfox.org/mozilla-central/source/ [common-shared.css](https://searchfox.org/mozilla-central/source/toolkit/themes/shared/in-content/common-shared.css) imports `tokens-brand.css` so that in-content/about: pages can make use of our brand values, while [global-shared.css](https://searchfox.org/mozilla-central/source/toolkit/themes/shared/global-shared.css), which styles the chrome, imports `tokens-platform.css` so that the chrome can access operating system and themeable values. -<div class="box"> - <div class="box vertical"> - <div class="box vertical align-center"> - <div class="post-it big"> - <a title="tokens-shared.css" href="https://searchfox.org/mozilla-central/source/toolkit/themes/shared/design-system/tokens-shared.css">tokens-shared.css</a> - </div> - </div> - <div class="box justify-center"> - ↙ ↘ - </div> - <div class="box justify-center"> - <div class="box vertical"> - <div class="post-it big"> - <a title="tokens-brand.css" href="https://searchfox.org/mozilla-central/source/toolkit/themes/shared/design-system/tokens-brand.css">tokens-brand.css</a> - </div> - <div class="box justify-center"> - ↓ - </div> - </div> - <div class="box vertical"> - <div class="post-it big"> - <a title="tokens-platform.css" href="https://searchfox.org/mozilla-central/source/toolkit/themes/shared/design-system/tokens-platform.css">tokens-platform.css</a> - </div> - <div class="box justify-center"> - ↓ - </div> - </div> - </div> - <div class="box justify-center"> - <div class="box vertical"> - <div class="post-it orange big"> - <a title="common-shared.css" href="https://searchfox.org/mozilla-central/source/toolkit/themes/shared/in-content/common-shared.css">common-shared.css</a> - </div> - <div class="box justify-center"> - ↓ - </div> - </div> - <div class="box vertical"> - <div class="post-it orange big"> - <a title="global-shared.css" href="https://searchfox.org/mozilla-central/source/toolkit/themes/shared/global-shared.css">global-shared.css</a> - </div> - <div class="box justify-center"> - ↓ - </div> - </div> - </div> - <div class="box justify-center"> - <div class="post-it blue big"> - Styles in-content (about:) pages - </div> - <div class="post-it blue big"> - Styles the chrome - </div> - </div> - </div> -</div> +![ + A diagram of the token files' hierarchy. \ + Tokens-shared.css is imported by both tokens-brand.css and tokens-platform.css. \ + There are two branches now, tokens-brand and tokens-platform. \ + For the tokens-brand branch, these tokens are then imported by common-shared.css which then styles in-content pages. \ + For the tokens-platform branch, these tokens are imported by global-shared.css which then styles the chrome. \ +](./img/token-files-hierarchy.png) #### `tokens-brand.css` This file is for token values specific to the brand, such as colors and @@ -612,7 +269,7 @@ For example, both the chrome and in-content pages make use of the same border-ra ### Tiers #### Base -Base design tokens act as the foundation for the design tokens collection. This tier defines our collection of raw styles that are used to form our semantic design token names. These tokens should not be used directly for styling UI as they don’t carry any meaning and just serve as a base for the design tokens structure. +Base design tokens represent the most basic, or foundational, groups of design tokens that point to the actual hard-coded values of the design system. They can be referenced to create more meaningful tokens. ```css /* tokens-shared.css */ @@ -622,7 +279,7 @@ Base design tokens act as the foundation for the design tokens collection. This ``` #### Application -Application design tokens represent the collection of semantic design tokens that actually give proper meaning to style choices. This tier relies on the agreement of the name and meaning behind these styles and their values. +Application design tokens represent the more semantic groups of design tokens that give meaning to base values based on their purpose or how/where they are applied. ```css /* tokens-brand.css */ @@ -630,9 +287,9 @@ Application design tokens represent the collection of semantic design tokens tha ``` #### Component -Component is the final tier. This tier is used in order to give meaning to identified style choices made within user interface components. While the "Application" tier can handle most if not all styling use cases, tier 3 helps encapsulate style decisions at the component level. +Component design tokens represent design tokens scoped to a specific component or element. While the "Application" tier can handle most if not all styling use cases, tier 3 helps encapsulate style decisions at the component level. -Component-level tokens should live at the component-level file (e.g. [moz-toggle.css](https://searchfox.org/mozilla-central/rev/02841791400cf7cf5760c0cfaf31f5d772624253/toolkit/content/widgets/moz-toggle/moz-toggle.css#34-50)), so that they can't be used outside of that specific component's domain. +Although some component-specific tokens for basic HTML elements (e.g. button) live in `tokens-shared.css` today, component-specific tokens should live at the component-level file (e.g. [moz-toggle.css](https://searchfox.org/mozilla-central/rev/02841791400cf7cf5760c0cfaf31f5d772624253/toolkit/content/widgets/moz-toggle/moz-toggle.css#34-50)) so that they can't be used outside of that specific component's domain. ```css /* moz-toggle.css */ @@ -676,29 +333,7 @@ Any semantic tokens within a scale, such as the `--font-size-root` example below --font-size-xxlarge: 2.2rem; /* 33px */ ``` -[Base](#base) tokens should be listed at the top of the file, with a heading indicating that they are Base: -```css -/* Base tokens */ -/* Do not use base tokens directly. Their names don't carry any specific meaning, and they are simply used to set foundations. Refer to Application tokens below. */ -/** Color **/ ---color-white: #ffffff; ---color-blue-05: #deeafc; ---color-blue-30: #73a7f3; -... -``` - -[Application](#application) tokens should be added after Base tokens, accompanied by its own heading as well: -```css -/* Application tokens */ -/** Border **/ ---border-radius-circle: 9999px; ---border-radius-small: 4px; ---border-width: 1px; -... -``` - ## Theming - For certain components, their high-contrast mode design will need styles that other modes do not (e.g. HCM relies on borders that do not exist in non-HCM). In those instances, we just add tokens under the high contrast mode media query rules. On the other hand, if something such as a color, does not apply to HCM contexts, then we add those design tokens under a "@media not (prefers-contrast)" query. ### Light and dark diff --git a/toolkit/themes/shared/design-system/docs/README.json-design-tokens.stories.md b/toolkit/themes/shared/design-system/docs/README.json-design-tokens.stories.md new file mode 100644 index 0000000000..1081481f9f --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/README.json-design-tokens.stories.md @@ -0,0 +1,290 @@ +# JSON design tokens +## Background +The benefit of storing design tokens with a platform-agnostic format such as JSON is that it can be converted or translated into other languages or tools (e.g CSS, Swift, Kotlin, Figma). + +## Quick start +`design-tokens.json` holds our source of truth for design tokens in `mozilla-central` under the [design-system](https://searchfox.org/mozilla-central/source/toolkit/themes/shared/design-system) folder in `toolkit/themes/shared`. The CSS design token files in that folder come from the JSON file. If you need to modify a design token file, you should be editing the JSON. + +In order for us to be able to define design tokens in one place (the JSON file) and allow all platforms to consume design tokens in their specific format, we use a build system called [Style Dictionary](https://amzn.github.io/style-dictionary/#/). + +Here's how to build design tokens for desktop: + +```sh +$ ./mach buildtokens +``` + +If successful, you should see Style Dictionary building all of our tokens files within the `design-system` folder. Otherwise, Style Dictionary can also generate helpful errors to help you debug. + +At the end, we're capable of transforming JSON notation into CSS: + +```json +{ + "color": { + "blue": { + "05": { + "value": "#deeafc" + }, + "30": { + "value": "#73a7f3" + }, + "50": { + "value": "#0060df" + }, + "60": { + "value": "#0250bb" + }, + "70": { + "value": "#054096" + }, + "80": { + "value": "#003070" + } + }, + }, +} +``` + +```css +--color-blue-05: #deeafc; +--color-blue-30: #73a7f3; +--color-blue-50: #0060df; +--color-blue-60: #0250bb; +--color-blue-70: #054096; +--color-blue-80: #003070; +``` + +Neat! + +## Testing + +We have basic tests in place to ensure that our built CSS files stay up to date +and that new tokens are properly categorized. These tests will fail if the JSON +is modified but the command to build the tokens CSS files doesn't run, or if the +tokens CSS files are modified directly without changing the JSON. + +Our tests are Node-based to allow us to install and work with the +`style-dictionary` library, and follow a format that has been used previously +for Devtools and New Tab tests. + +You can run the tests locally using the following command: + +```sh +$ ./mach npm test --prefix=toolkit/themes/shared/design-system +``` + +## JSON format deep dive + +Style Dictionary works by ingesting JSON files with tokens data and performing various platform-specific transformations to output token files formatted for different languages. The library has certain quirks and limitations that we had to take into consideration when coming up with a JSON format for representing our tokens. The following is a bit of a "how to" guide for reading and adding to [`design-tokens.json`](https://searchfox.org/mozilla-central/source/toolkit/themes/shared/design-system/design-tokens.json) for anyone who needs to consume our tokens or add new tokens. + +### Naming + +When going from token name to JSON, each place we would insert a hyphen, underscore, or case change in a variable name translates to a layer of nesting in our JSON. That means a token with the CSS variable name `--border-radius-circle` would be represented as: + +```json +"border": { + "radius": { + "circle": { + "value": "9999px" + } + } +} +``` + +The `value` key is required as this is the indicator [Style Dictionary looks for](https://amzn.github.io/style-dictionary/#/architecture?id=_4a-transform-the-tokens) to know what sections of the JSON to parse into distinct tokens. It gets omitted from the final variable name. + +For simple cases `value` will be either a string or a number, but when we have to take media queries or themes into account `value` will be an object. Examples of these cases will be explained in more detail below. + +### `@base` + +When reading through our JSON you will see many instances where we have used an `@base` key, always appearing right before a `value` key indicating that everything up to that point should be parsed as a token. For example our `--font-weight` CSS token is represented like this in our JSON: + +```json +"font": { + "weight": { + "@base": { + "value": "normal" + } + } +} +``` +This is a workaround for a [known](https://github.com/amzn/style-dictionary/issues/119) [limitation](https://github.com/amzn/style-dictionary/issues/366) of Style Dictionary where it doesn't support nested token names that appear after a `value` key. If we want to have both `--font-weight` and `--font-weight-bold` CSS tokens they need to be represented as distinct objects with their own `value`s: + +```json +"font": { + "weight": { + "@base": { + "value": "normal" + }, + "bold": { + "value": 600 + } + } +} +``` +`@base` is a special indicator we chose to enable Style Dictionary to parse our tokens correctly while still generating our desired variable names. Much like `value`, it ultimately [gets removed](https://searchfox.org/mozilla-central/rev/159929cd10b8fba135c72a497d815ab2dd5a521c/toolkit/themes/shared/design-system/tokens-config.js#94-96) from the final token name. + +You will also see many places where `@base` is referenced in a value definition. This is a bit of a gotcha - even though `@base` isn't part of the token name, we still need to include it when using Style Dictionary's syntax for [variable references/aliases](https://amzn.github.io/style-dictionary/#/tokens?id=referencing-aliasing). That means the following CSS: + +```css +--input-text-min-height: var(--button-min-height); +``` + +Will look like this in our JSON: + +```json +"input": { + "text": { + "min": { + "height": { + "value": "{button.min.height.@base}" + } + } + } +} +``` + +### High Contrast Mode (HCM) media queries + +We need to be able to change our token values to support both the `prefers-contrast` and `forced-colors` [media queries](https://firefox-source-docs.mozilla.org/accessible/HCMMediaQueries.html). We employ `prefersContrast` and `forcedColors` keys nested in a token's `value` key to indicate that we need to generate a media query entry for that token. For example this JSON: + +```json +"border": { + "color": { + "interactive": { + "@base": { + "value": { + "prefersContrast": "{text.color.@base}", + "forcedColors": "ButtonText", + } + } + }, + } +} +``` +results in the following CSS: + +```css +/* tokens-shared.css */ + +@layer tokens-prefers-contrast { + @media (prefers-contrast) { + :root, + :host(.anonymous-content-host) { + /** Border **/ + --border-color-interactive: var(--text-color); + } + } +} + +@layer tokens-forced-colors { + @media (forced-colors) { + :root, + :host(.anonymous-content-host) { + /** Border **/ + --border-color-interactive: ButtonText; + } + } +} +``` + +### Theming + +Our JSON supports two dimensions of theming designed around the needs of the Firefox desktop client; light and dark themes, and brand and platform themes. + +#### Light and dark themes + +We employ `light` and `dark` keys in our `value` objects to indicate when a given token should have different values in our light and dark themes: + +```json +"background": { + "color": { + "box": { + "value": { + "light": "{color.white}", + "dark": "{color.gray.80}", + } + }, + } +} +``` + +The above JSON communicates that `--background-color-box` should have the value of `var(--color-white)` in our light theme and `var(--color-gray-80)` in our dark theme. + +If a token has the same value for both the light and dark themes it will either have a string or number as its `value` or it will use a `default` key: + +```json +"button": { + "background": { + "color": { + "disabled": { + "value": { + "default": "{button.background.color.@base}", + } + } + } + } +} +``` + +The above JSON indicates that `--button-background-color-disabled` will have the value of `var(--button-background-color)` regardless of theme. + +#### Brand and platform themes + +The Firefox desktop client consists of [two distinct surfaces](https://acorn.firefox.com/latest/resources/browser-anatomy/desktop-ZaxCgqkt#section-anatomy-fd); "the chrome," or the UI of the browser application that surrounds web pages, and the web content itself which is often referred to as "in-content." Firefox UI development spans both surfaces since our `about:` pages are "in-content" pages. In our design system we distinguish between these surfaces by using the terminology of `platform` vs `brand`. Chrome specific token values live in [`tokens-platform.css`](https://searchfox.org/mozilla-central/source/toolkit/themes/shared/design-system/tokens-platform.css) and in-content specific token values live in [`tokens-brand.css`](https://searchfox.org/mozilla-central/source/toolkit/themes/shared/design-system/tokens-brand.css). + +We use `platform` and `brand` keys in our JSON to indicate when a token has a surface-specific value. For example this token definition: + +```json +"text": { + "color": { + "@base": { + "value": { + "brand": { + "light": "{color.gray.100}", + "dark": "{color.gray.05}" + }, + "platform": { + "default": "currentColor" + } + } + }, + } +} +``` +communicates that `--text-color` should have the value `currentColor` in `tokens-platform.css` for chrome surfaces, and the value `light-dark(var(--color-gray-100), var(--color-gray-05))` in `tokens-brand.css` for in-content pages. The resulting CSS spans multiple files: + +```css +/* tokens-platform.css */ +@layer tokens-foundation { + :root, + :host(.anonymous-content-host) { + /** Text **/ + --text-color: currentColor; + } +} +``` + +```css +/* tokens-brand.css */ +@layer tokens-foundation { + :root, + :host(.anonymous-content-host) { + /** Text **/ + --text-color: light-dark(var(--color-gray-100), var(--color-gray-05)); + } +} +``` + +### Adding new tokens + +In order to add a new token you will need to make changes to `design-tokens.json` - any files generated based on our JSON token definitions should never be modified directly. You should be able to work backwards from the variable name in whatever language you're working with to figure out the correct JSON structure. + +Once you've made your changes, you can generate the new tokens CSS files by running `./mach buildtokens`. + +If you encounter errors or get unexpected results when running the build command it may be because: + +* You forgot to nest everything in a `value` key and consequently Style Dictionary is not parsing your tokens correctly, or; +* You omitted `@base` when referencing another token, or; +* You are referencing another token that you haven't defined in the JSON yet - Style Dictionary should give descriptive error messages in this case. + +If you need any help feel free to reach out in #firefox-reusable-components on Slack or #reusable-components on Matrix. diff --git a/toolkit/themes/shared/design-system/docs/img/categories-concept-example.png b/toolkit/themes/shared/design-system/docs/img/categories-concept-example.png Binary files differnew file mode 100644 index 0000000000..6fc7681898 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/categories-concept-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/categories-property-example.png b/toolkit/themes/shared/design-system/docs/img/categories-property-example.png Binary files differnew file mode 100644 index 0000000000..0304beb049 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/categories-property-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/categories-type-example.png b/toolkit/themes/shared/design-system/docs/img/categories-type-example.png Binary files differnew file mode 100644 index 0000000000..6a8cba0377 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/categories-type-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/component-name-example.png b/toolkit/themes/shared/design-system/docs/img/component-name-example.png Binary files differnew file mode 100644 index 0000000000..14aaf57516 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/component-name-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/ecosystem-domain.png b/toolkit/themes/shared/design-system/docs/img/ecosystem-domain.png Binary files differnew file mode 100644 index 0000000000..a2024fd6bc --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/ecosystem-domain.png diff --git a/toolkit/themes/shared/design-system/docs/img/modifiers-border-radius-example.png b/toolkit/themes/shared/design-system/docs/img/modifiers-border-radius-example.png Binary files differnew file mode 100644 index 0000000000..fe94e2855e --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/modifiers-border-radius-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/modifiers-font-size-example.png b/toolkit/themes/shared/design-system/docs/img/modifiers-font-size-example.png Binary files differnew file mode 100644 index 0000000000..8df20aa7b6 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/modifiers-font-size-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/modifiers-naming-guidelines.png b/toolkit/themes/shared/design-system/docs/img/modifiers-naming-guidelines.png Binary files differnew file mode 100644 index 0000000000..3197e53cc5 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/modifiers-naming-guidelines.png diff --git a/toolkit/themes/shared/design-system/docs/img/modifiers-scale-example.png b/toolkit/themes/shared/design-system/docs/img/modifiers-scale-example.png Binary files differnew file mode 100644 index 0000000000..6d39a1a740 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/modifiers-scale-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/modifiers-state-example.png b/toolkit/themes/shared/design-system/docs/img/modifiers-state-example.png Binary files differnew file mode 100644 index 0000000000..2d24c1af28 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/modifiers-state-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/modifiers-variant-example.png b/toolkit/themes/shared/design-system/docs/img/modifiers-variant-example.png Binary files differnew file mode 100644 index 0000000000..bd854d923e --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/modifiers-variant-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/nested-element-example.png b/toolkit/themes/shared/design-system/docs/img/nested-element-example.png Binary files differnew file mode 100644 index 0000000000..8f6db5c329 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/nested-element-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/objects-pattern-example.png b/toolkit/themes/shared/design-system/docs/img/objects-pattern-example.png Binary files differnew file mode 100644 index 0000000000..f26500d131 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/objects-pattern-example.png diff --git a/toolkit/themes/shared/design-system/docs/img/taxonomy-overview.png b/toolkit/themes/shared/design-system/docs/img/taxonomy-overview.png Binary files differnew file mode 100644 index 0000000000..d5df5622e4 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/taxonomy-overview.png diff --git a/toolkit/themes/shared/design-system/docs/img/token-files-hierarchy.png b/toolkit/themes/shared/design-system/docs/img/token-files-hierarchy.png Binary files differnew file mode 100644 index 0000000000..423fc4ccf5 --- /dev/null +++ b/toolkit/themes/shared/design-system/docs/img/token-files-hierarchy.png diff --git a/toolkit/themes/shared/design-system/package-lock.json b/toolkit/themes/shared/design-system/package-lock.json new file mode 100644 index 0000000000..b45ca87094 --- /dev/null +++ b/toolkit/themes/shared/design-system/package-lock.json @@ -0,0 +1,1405 @@ +{ + "name": "design-system", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "design-system", + "version": "1.0.0", + "license": "MPL-2.0", + "devDependencies": { + "style-dictionary": "^3.9.2" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "dev": true, + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-dictionary": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/style-dictionary/-/style-dictionary-3.9.2.tgz", + "integrity": "sha512-M2pcQ6hyRtqHOh+NyT6T05R3pD/gwNpuhREBKvxC1En0vyywx+9Wy9nXWT1SZ9ePzv1vAo65ItnpA16tT9ZUCg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "change-case": "^4.1.2", + "commander": "^8.3.0", + "fs-extra": "^10.0.0", + "glob": "^10.3.10", + "json5": "^2.2.2", + "jsonc-parser": "^3.0.0", + "lodash": "^4.17.15", + "tinycolor2": "^1.4.1" + }, + "bin": { + "style-dictionary": "bin/style-dictionary" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + } + }, + "dependencies": { + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "dev": true, + "requires": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "requires": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, + "sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + } + } + }, + "style-dictionary": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/style-dictionary/-/style-dictionary-3.9.2.tgz", + "integrity": "sha512-M2pcQ6hyRtqHOh+NyT6T05R3pD/gwNpuhREBKvxC1En0vyywx+9Wy9nXWT1SZ9ePzv1vAo65ItnpA16tT9ZUCg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "change-case": "^4.1.2", + "commander": "^8.3.0", + "fs-extra": "^10.0.0", + "glob": "^10.3.10", + "json5": "^2.2.2", + "jsonc-parser": "^3.0.0", + "lodash": "^4.17.15", + "tinycolor2": "^1.4.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + }, + "upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + } + } +} diff --git a/toolkit/themes/shared/design-system/package.json b/toolkit/themes/shared/design-system/package.json new file mode 100644 index 0000000000..2ced9f47bb --- /dev/null +++ b/toolkit/themes/shared/design-system/package.json @@ -0,0 +1,13 @@ +{ + "name": "design-system", + "version": "1.0.0", + "description": "Package file for node modules used to create our CSS tokens from a JSON source of truth.", + "license": "MPL-2.0", + "scripts": { + "build": "(npm ls || npm ci) && style-dictionary build --config ./tokens-config.js && prettier ./tokens-storybook.mjs --write", + "test": "(npm ls || npm ci) && node tests/try-runner.js" + }, + "devDependencies": { + "style-dictionary": "^3.9.2" + } +} diff --git a/toolkit/themes/shared/design-system/tests/try-runner.js b/toolkit/themes/shared/design-system/tests/try-runner.js new file mode 100644 index 0000000000..67ddfe3733 --- /dev/null +++ b/toolkit/themes/shared/design-system/tests/try-runner.js @@ -0,0 +1,119 @@ +/* eslint-disable no-console */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* eslint-env node */ + +/* + * A small test runner/reporter for node-based tests, + * which are run via taskcluster node(debugger). + * + * Adapted from: + * https://searchfox.org/mozilla-central/rev/9cd4ea81e27db6b767f1d9bbbcf47da238dd64fa/browser/components/newtab/bin/try-runner.js + */ + +const { readFileSync, rmSync } = require("fs"); +const chalk = require("chalk"); +const path = require("path"); +const StyleDictionary = require("style-dictionary"); +const config = require("../tokens-config.js"); + +const TEST_BUILD_PATH = "tests/build/css/"; + +function buildFilesWithTestConfig() { + // Use our real config, just modify some values for the test. This prevents us + // from re-building the CSS files that get checked in when we run the tests. + let testConfig = Object.assign({}, config); + testConfig.source = [path.join(__dirname, "../design-tokens.json")]; + testConfig.platforms.css.buildPath = TEST_BUILD_PATH; + + // This is effectively the same as running `npm run build` and allows us to + // use the modified config. + StyleDictionary.extend(testConfig).buildAllPlatforms(); +} + +function logErrors(tool, errors) { + for (const error of errors) { + console.log(`TEST-UNEXPECTED-FAIL | ${tool} | ${error}`); + } + return errors; +} + +function logStart(name) { + console.log(`TEST-START | ${name}`); +} + +const FILE_PATHS = { + "tokens-brand.css": { + path: path.join("tokens-brand.css"), + testPath: path.join(TEST_BUILD_PATH, "tokens-brand.css"), + }, + "tokens-platform.css": { + path: path.join("tokens-platform.css"), + testPath: path.join(TEST_BUILD_PATH, "tokens-platform.css"), + }, + "tokens-shared.css": { + path: path.join("tokens-shared.css"), + testPath: path.join(TEST_BUILD_PATH, "tokens-shared.css"), + }, +}; + +const tests = { + // Verify the CSS files build successfully and are up to date. + buildCSS() { + logStart("build CSS"); + + let errors = []; + let currentCSS = {}; + + // Read the contents of our built CSS files. + for (let [fileName, { path }] of Object.entries(FILE_PATHS)) { + currentCSS[fileName] = readFileSync(path, "utf8"); + } + + try { + buildFilesWithTestConfig(); + } catch { + errors.push("CSS build did not run successfully"); + } + + // Build CSS files to the test directory and compare them to the current CSS + // files that get checked in. If the contents don't match we either forgot + // to build the files after making a change, or edited the CSS files directly. + for (let [fileName, { testPath }] of Object.entries(FILE_PATHS)) { + let builtCSS = readFileSync(testPath, "utf8"); + + if (builtCSS !== currentCSS[fileName]) { + errors.push(`${fileName} is out of date`); + } + + if (builtCSS.includes("/** Unspecified **/")) { + errors.push( + "Tokens present in the 'Unspecified' section. Please update TOKEN_SECTIONS in tokens-config.js" + ); + } + } + + logErrors("build CSS", errors); + rmSync("tests/build", { recursive: true, force: true }); + return errors.length === 0; + }, +}; + +(function runTests() { + let results = []; + + for (let testName of Object.keys(tests)) { + results.push([testName, tests[testName]()]); + } + + for (const [name, result] of results) { + // Colorize output based on result. + console.log(result ? chalk.green(`✓ ${name}`) : chalk.red(`✗ ${name}`)); + } + + const success = results.every(([, result]) => result); + process.exitCode = success ? 0 : 1; + console.log("CODE", process.exitCode); +})(); diff --git a/toolkit/themes/shared/design-system/tokens-brand.css b/toolkit/themes/shared/design-system/tokens-brand.css index 0ab73834d0..4574cf405a 100644 --- a/toolkit/themes/shared/design-system/tokens-brand.css +++ b/toolkit/themes/shared/design-system/tokens-brand.css @@ -2,56 +2,49 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* DO NOT EDIT this file directly, instead modify design-tokens.json + * and run `npm run build` to see your changes. */ + @import url("chrome://global/skin/design-system/tokens-shared.css"); @layer tokens-foundation { :root, :host(.anonymous-content-host) { + /** Attention Dot **/ + --attention-dot-color: light-dark(#2ac3a2, #54ffbd); + + /** Background Color **/ + --background-color-canvas: light-dark(var(--color-white), var(--color-gray-90)); + /** Border **/ - --border-interactive-color: light-dark(var(--color-gray-60), var(--color-gray-50)); + --border-color-interactive: light-dark(var(--color-gray-60), var(--color-gray-50)); /** Button **/ - /* TODO Bug 1821203 - Gray use needs to be consolidated */ - --button-background-color: color-mix(in srgb, currentColor 7%, transparent); + --button-background-color: color-mix(in srgb, currentColor 7%, transparent); /* TODO Bug 1821203 - Gray use needs to be consolidated */ --button-background-color-hover: color-mix(in srgb, currentColor 14%, transparent); --button-background-color-active: color-mix(in srgb, currentColor 21%, transparent); - --button-border-color-primary: transparent; --button-text-color: light-dark(var(--color-gray-100), var(--color-gray-05)); --button-text-color-primary: light-dark(var(--color-gray-05), var(--color-gray-100)); - /** Link **/ - --link-color: var(--color-accent-primary); - --link-color-hover: var(--color-accent-primary-hover); - --link-color-active: var(--color-accent-primary-active); - --link-color-visited: var(--link-color); - /** Color **/ --color-accent-primary: light-dark(var(--color-blue-50), var(--color-cyan-50)); --color-accent-primary-hover: light-dark(var(--color-blue-60), var(--color-cyan-30)); --color-accent-primary-active: light-dark(var(--color-blue-70), var(--color-cyan-20)); - --color-canvas: light-dark(var(--color-white), var(--color-gray-90)); - /** Font size **/ - --font-size-root: 15px; /* Set at the `:root`. Do not use */ - --font-size-small: 0.867rem; /* 13px */ - --font-size-large: 1.133rem; /* 17px */ - --font-size-xlarge: 1.467rem; /* 22px */ - --font-size-xxlarge: 1.6rem; /* 24px */ + /** Font Size **/ + --font-size-root: 15px; + --font-size-small: 0.867rem; + --font-size-large: 1.133rem; + --font-size-xlarge: 1.467rem; + --font-size-xxlarge: 1.6rem; + + /** Link **/ + --link-color: var(--color-accent-primary); + --link-color-hover: var(--color-accent-primary-hover); + --link-color-active: var(--color-accent-primary-active); + --link-color-visited: var(--link-color); /** Text **/ --text-color: light-dark(var(--color-gray-100), var(--color-gray-05)); } } - -@layer tokens-prefers-contrast { - @media (prefers-contrast) { - :root, - :host(.anonymous-content-host) { - /* Border */ - --border-interactive-color: var(--text-color); - --border-interactive-color-hover: var(--border-interactive-color); - --border-interactive-color-active: var(--border-interactive-color); - --border-interactive-color-disabled: var(--border-interactive-color); - } - } -} diff --git a/toolkit/themes/shared/design-system/tokens-config.js b/toolkit/themes/shared/design-system/tokens-config.js new file mode 100644 index 0000000000..81d92d8faa --- /dev/null +++ b/toolkit/themes/shared/design-system/tokens-config.js @@ -0,0 +1,547 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* eslint-env node */ + +const StyleDictionary = require("style-dictionary"); +const { createPropertyFormatter } = StyleDictionary.formatHelpers; +const TOKEN_SECTIONS = { + "Attention Dot": "attention-dot", + "Background Color": "background-color", + Border: "border", + "Box Shadow": "box-shadow", + Button: "button", + Checkbox: "checkbox", + Color: ["brand-color", "color", "platform-color"], + "Focus Outline": "focus-outline", + "Font Size": "font-size", + "Font Weight": "font-weight", + Icon: "icon", + "Input - Text": "input-text", + Link: "link", + "Outline Color": "outline-color", + Size: "size", + Space: "space", + Text: "text", + Unspecified: "", +}; +const TSHIRT_ORDER = [ + "circle", + "xxxsmall", + "xxsmall", + "xsmall", + "small", + "medium", + "large", + "xlarge", + "xxlarge", + "xxxlarge", +]; +const STATE_ORDER = [ + "base", + "default", + "root", + "hover", + "active", + "focus", + "disabled", +]; + +/** + * Adds the Mozilla Public License header in one comment and + * how to make changes in the generated output files via the + * design-tokens.json file in another comment. Also imports + * tokens-shared.css when applicable. + * + * @param {string} surface + * Desktop surface, either "brand" or "platform". Determines + * whether or not we need to import tokens-shared.css. + * @returns {string} Formatted comment header string + */ +let customFileHeader = ({ surface, platform }) => { + let licenseString = [ + "/* This Source Code Form is subject to the terms of the Mozilla Public", + " * License, v. 2.0. If a copy of the MPL was not distributed with this", + " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */", + ].join("\n"); + + let commentString = [ + "/* DO NOT EDIT this file directly, instead modify design-tokens.json", + " * and run `npm run build` to see your changes. */", + ].join("\n"); + + let cssImport = surface + ? `@import url("chrome://global/skin/design-system/tokens-shared.css");\n\n` + : ""; + let layerString = + !surface && !platform + ? `@layer tokens-foundation, tokens-prefers-contrast, tokens-forced-colors;\n\n` + : ""; + + return [ + licenseString + "\n\n" + commentString + "\n\n" + cssImport + layerString, + ]; +}; + +const NEST_MEDIA_QUERIES_COMMENT = `/* Bug 1879900: Can't nest media queries inside of :host, :root selector + until Bug 1879349 lands */`; + +const MEDIA_QUERY_PROPERTY_MAP = { + "forced-colors": "forcedColors", + "prefers-contrast": "prefersContrast", +}; + +function formatBaseTokenNames(str) { + return str.replaceAll(/(?<tokenName>\w+)-base(?=\b)/g, "$<tokenName>"); +} + +/** + * Creates a surface-specific formatter. The formatter is used to build + * our different CSS files, including "prefers-contrast" and "forced-colors" + * media queries. See more at + * https://amzn.github.io/style-dictionary/#/formats?id=formatter + * + * @param {string} surface + * Which desktop area we are generating CSS for. + * Either "brand" (i.e. in-content) or "platform" (i.e. chrome). + * @returns {Function} - Formatter function that returns a CSS string. + */ +const createDesktopFormat = surface => args => { + return formatBaseTokenNames( + customFileHeader({ surface }) + + formatTokens({ + surface, + args, + }) + + formatTokens({ + mediaQuery: "prefers-contrast", + surface, + args, + }) + + formatTokens({ + mediaQuery: "forced-colors", + surface, + args, + }) + ); +}; + +/** + * Formats a subset of tokens into CSS. Wraps token CSS in a media query when + * applicable. + * + * @param {object} tokenArgs + * @param {string} [tokenArgs.mediaQuery] + * Media query formatted CSS should be wrapped in. This is used + * to determine what property we are parsing from the token values. + * @param {string} [tokenArgs.surface] + * Specifies a desktop surface, either "brand" or "platform". + * @param {object} tokenArgs.args + * Formatter arguments provided by style-dictionary. See more at + * https://amzn.github.io/style-dictionary/#/formats?id=formatter + * @returns {string} Tokens formatted into a CSS string. + */ +function formatTokens({ mediaQuery, surface, args }) { + let prop = MEDIA_QUERY_PROPERTY_MAP[mediaQuery] ?? "default"; + let dictionary = Object.assign({}, args.dictionary); + let tokens = []; + + dictionary.allTokens.forEach(token => { + let originalVal = getOriginalTokenValue(token, prop, surface); + if (originalVal != undefined) { + let formattedToken = transformTokenValue(token, originalVal, dictionary); + tokens.push(formattedToken); + } + }); + + if (!tokens.length) { + return ""; + } + + dictionary.allTokens = dictionary.allProperties = tokens; + + let formattedVars = formatVariables({ + format: "css", + dictionary, + outputReferences: args.options.outputReferences, + formatting: { + indentation: mediaQuery ? " " : " ", + }, + }); + + let layer = `tokens-${mediaQuery ?? "foundation"}`; + // Weird spacing below is unfortunately necessary for formatting the built CSS. + if (mediaQuery) { + return ` +${NEST_MEDIA_QUERIES_COMMENT} +@layer ${layer} { + @media (${mediaQuery}) { + :root, + :host(.anonymous-content-host) { +${formattedVars} + } + } +} +`; + } + + return `@layer ${layer} { + :root, + :host(.anonymous-content-host) { +${formattedVars} + } +} +`; +} + +/** + * Finds the original value of a token for a given media query and surface. + * + * @param {object} token - Token object parsed by style-dictionary. + * @param {string} prop - Name of the property we're querying for. + * @param {string} surface + * The desktop surface we're generating CSS for, either "brand" or "platform". + * @returns {string} The original token value based on our parameters. + */ +function getOriginalTokenValue(token, prop, surface) { + if (surface) { + return token.original.value[surface]?.[prop]; + } else if (prop == "default" && typeof token.original.value != "object") { + return token.original.value; + } + return token.original.value?.[prop]; +} + +/** + * Updates a token's value to the relevant original value after resolving + * variable references. + * + * @param {object} token - Token object parsed from JSON by style-dictionary. + * @param {string} originalVal + * Original value of the token for the combination of surface and media query. + * @param {object} dictionary + * Object of transformed tokens and helper fns provided by style-dictionary. + * @returns {object} Token object with an updated value. + */ +function transformTokenValue(token, originalVal, dictionary) { + let value = originalVal; + if (dictionary.usesReference(value)) { + dictionary.getReferences(value).forEach(ref => { + value = value.replace(`{${ref.path.join(".")}}`, `var(--${ref.name})`); + }); + } + return { ...token, value }; +} + +/** + * Creates a light-dark transform that works for a given surface. Registers + * the transform with style-dictionary and returns the transform's name. + * + * @param {string} surface + * The desktop surface we're generating CSS for, either "brand", "platform", + * or "shared". + * @returns {string} Name of the transform that was registered. + */ +const createLightDarkTransform = surface => { + let name = `lightDarkTransform/${surface}`; + + // Matcher function for determining if a token's value needs to undergo + // a light-dark transform. + let matcher = token => { + if (surface != "shared") { + return ( + token.original.value[surface]?.light && + token.original.value[surface]?.dark + ); + } + return token.original.value.light && token.original.value.dark; + }; + + // Function that uses the token's original value to create a new "default" + // light-dark value and updates the original value object. + let transformer = token => { + if (surface != "shared") { + let lightDarkVal = `light-dark(${token.original.value[surface].light}, ${token.original.value[surface].dark})`; + token.original.value[surface].default = lightDarkVal; + return token.value; + } + let value = `light-dark(${token.original.value.light}, ${token.original.value.dark})`; + token.original.value.default = value; + return value; + }; + + StyleDictionary.registerTransform({ + type: "value", + transitive: true, + name, + matcher, + transformer, + }); + + return name; +}; + +/** + * Format the tokens dictionary to a string. This mostly defers to + * StyleDictionary.createPropertyFormatter but first it sorts the tokens based + * on the groupings in TOKEN_SECTIONS and adds comment headers to CSS output. + * + * @param {object} options + * Options for tokens to format. + * @param {string} options.format + * The format to output. Supported: "css" + * @param {object} options.dictionary + * The tokens dictionary. + * @param {string} options.outputReferences + * Whether to output variable references. + * @param {object} options.formatting + * The formatting settings to be passed to createPropertyFormatter. + * @returns {string} The formatted tokens. + */ +function formatVariables({ format, dictionary, outputReferences, formatting }) { + let lastSection = []; + let propertyFormatter = createPropertyFormatter({ + outputReferences, + dictionary, + format, + formatting, + }); + + let outputParts = []; + let remainingTokens = [...dictionary.allTokens]; + let isFirst = true; + + function tokenParts(name) { + let lastDash = name.lastIndexOf("-"); + let suffix = name.substring(lastDash + 1); + if (TSHIRT_ORDER.includes(suffix) || STATE_ORDER.includes(suffix)) { + return [name.substring(0, lastDash), suffix]; + } + return [name, ""]; + } + + for (let [label, selector] of Object.entries(TOKEN_SECTIONS)) { + let sectionMatchers = Array.isArray(selector) ? selector : [selector]; + let sectionParts = []; + + remainingTokens = remainingTokens.filter(token => { + if ( + sectionMatchers.some(m => + m.test ? m.test(token.name) : token.name.startsWith(m) + ) + ) { + sectionParts.push(token); + return false; + } + return true; + }); + + if (sectionParts.length) { + sectionParts.sort((a, b) => { + let aName = formatBaseTokenNames(a.name); + let bName = formatBaseTokenNames(b.name); + let [aToken, aSuffix] = tokenParts(aName); + let [bToken, bSuffix] = tokenParts(bName); + if (aSuffix || bSuffix) { + if (aToken == bToken) { + let aSize = TSHIRT_ORDER.indexOf(aSuffix); + let bSize = TSHIRT_ORDER.indexOf(bSuffix); + if (aSize != -1 && bSize != -1) { + return aSize - bSize; + } + let aState = STATE_ORDER.indexOf(aSuffix); + let bState = STATE_ORDER.indexOf(bSuffix); + if (aState != -1 && bState != -1) { + return aState - bState; + } + } + } + return aToken.localeCompare(bToken, undefined, { numeric: true }); + }); + + let headingParts = []; + if (!isFirst) { + headingParts.push(""); + } + isFirst = false; + + let sectionLevel = "**"; + let labelParts = label.split("/"); + for (let i = 0; i < labelParts.length; i++) { + if (labelParts[i] != lastSection[i]) { + headingParts.push( + `${formatting.indentation}/${sectionLevel} ${labelParts[i]} ${sectionLevel}/` + ); + } + sectionLevel += "*"; + } + lastSection = labelParts; + + outputParts = outputParts.concat( + headingParts.concat(sectionParts.map(propertyFormatter)) + ); + } + } + + return outputParts.join("\n"); +} + +// Easy way to grab variable values later for display. +let variableLookupTable = {}; + +function storybookJSFormat(args) { + let dictionary = Object.assign({}, args.dictionary); + let resolvedTokens = dictionary.allTokens.map(token => { + let tokenVal = resolveReferences(dictionary, token.original); + return { + name: token.name, + ...tokenVal, + }; + }); + dictionary.allTokens = dictionary.allProperties = resolvedTokens; + + let parsedData = JSON.parse( + formatBaseTokenNames( + StyleDictionary.format["javascript/module-flat"]({ + ...args, + dictionary, + }) + ) + .trim() + .replaceAll(/(^module\.exports\s*=\s*|\;$)/g, "") + ); + let storybookTables = formatTokensTablesData(parsedData); + + return `${customFileHeader({ platform: "storybook" })} + export const storybookTables = ${JSON.stringify(storybookTables)}; + + export const variableLookupTable = ${JSON.stringify(variableLookupTable)}; + `; +} + +function resolveReferences(dictionary, originalVal) { + let resolvedValues = {}; + Object.entries(originalVal).forEach(([key, value]) => { + if (typeof value === "object" && value != null) { + resolvedValues[key] = resolveReferences(dictionary, value); + } else { + let resolvedVal = getValueWithReferences(dictionary, value); + resolvedValues[key] = resolvedVal; + } + }); + return resolvedValues; +} + +function getValueWithReferences(dictionary, value) { + let valWithRefs = value; + if (dictionary.usesReference(value)) { + dictionary.getReferences(value).forEach(ref => { + valWithRefs = valWithRefs.replace( + `{${ref.path.join(".")}}`, + `var(--${ref.name})` + ); + }); + } + return valWithRefs; +} + +function formatTokensTablesData(tokensData) { + let tokensTables = {}; + Object.entries(tokensData).forEach(([key, value]) => { + variableLookupTable[key] = value; + let formattedToken = { + value, + name: `--${key}`, + }; + + let tableName = getTableName(key); + if (tokensTables[tableName]) { + tokensTables[tableName].push(formattedToken); + } else { + tokensTables[tableName] = [formattedToken]; + } + }); + return tokensTables; +} + +const SINGULAR_TABLE_CATEGORIES = [ + "button", + "color", + "link", + "size", + "space", + "opacity", + "outline", + "padding", + "margin", +]; + +function getTableName(tokenName) { + let replacePattern = /^(button-|input-text-|focus-|checkbox-)/; + if (tokenName.match(replacePattern)) { + tokenName = tokenName.replace(replacePattern, ""); + } + let [category, type] = tokenName.split("-"); + return SINGULAR_TABLE_CATEGORIES.includes(category) || !type + ? category + : `${category}-${type}`; +} + +module.exports = { + source: ["design-tokens.json"], + format: { + "css/variables/shared": createDesktopFormat(), + "css/variables/brand": createDesktopFormat("brand"), + "css/variables/platform": createDesktopFormat("platform"), + "javascript/storybook": storybookJSFormat, + }, + platforms: { + css: { + options: { + outputReferences: true, + showFileHeader: false, + }, + transforms: [ + ...StyleDictionary.transformGroup.css, + ...["shared", "platform", "brand"].map(createLightDarkTransform), + ], + files: [ + { + destination: "tokens-shared.css", + format: "css/variables/shared", + }, + { + destination: "tokens-brand.css", + format: "css/variables/brand", + filter: token => + typeof token.original.value == "object" && + token.original.value.brand, + }, + { + destination: "tokens-platform.css", + format: "css/variables/platform", + filter: token => + typeof token.original.value == "object" && + token.original.value.platform, + }, + ], + }, + storybook: { + options: { + outputReferences: true, + showFileHeader: false, + }, + transforms: [ + ...StyleDictionary.transformGroup.css, + ...["shared", "platform", "brand"].map(createLightDarkTransform), + ], + files: [ + { + destination: "tokens-storybook.mjs", + format: "javascript/storybook", + }, + ], + }, + }, +}; diff --git a/toolkit/themes/shared/design-system/tokens-platform.css b/toolkit/themes/shared/design-system/tokens-platform.css index 921f5fd860..d3a48f8047 100644 --- a/toolkit/themes/shared/design-system/tokens-platform.css +++ b/toolkit/themes/shared/design-system/tokens-platform.css @@ -2,16 +2,25 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* DO NOT EDIT this file directly, instead modify design-tokens.json + * and run `npm run build` to see your changes. */ + @import url("chrome://global/skin/design-system/tokens-shared.css"); @layer tokens-foundation { :root, :host(.anonymous-content-host) { + /** Attention Dot **/ + --attention-dot-color: AccentColor; + + /** Background Color **/ + --background-color-canvas: Canvas; + /** Border **/ - --border-interactive-color: color-mix(in srgb, currentColor 15%, var(--color-gray-60)); + --border-color-interactive: color-mix(in srgb, currentColor 15%, var(--color-gray-60)); /** Button **/ - --button-background-color: var(--button-bgcolor); + --button-background-color: var(--button-bgcolor); /* TODO Bug 1821203 - Gray use needs to be consolidated */ --button-background-color-hover: var(--button-hover-bgcolor); --button-background-color-active: var(--button-active-bgcolor); --button-text-color: var(--button-color); @@ -21,9 +30,8 @@ --color-accent-primary: var(--button-primary-bgcolor, AccentColor); --color-accent-primary-hover: var(--button-primary-hover-bgcolor); --color-accent-primary-active: var(--button-primary-active-bgcolor); - --color-canvas: Canvas; - /** Font size **/ + /** Font Size **/ --font-size-root: unset; --font-size-small: unset; --font-size-large: unset; diff --git a/toolkit/themes/shared/design-system/tokens-shared.css b/toolkit/themes/shared/design-system/tokens-shared.css index 0c074ca20e..42b33f5e0d 100644 --- a/toolkit/themes/shared/design-system/tokens-shared.css +++ b/toolkit/themes/shared/design-system/tokens-shared.css @@ -2,15 +2,95 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* DO NOT EDIT this file directly, instead modify design-tokens.json + * and run `npm run build` to see your changes. */ + @layer tokens-foundation, tokens-prefers-contrast, tokens-forced-colors; @layer tokens-foundation { :root, :host(.anonymous-content-host) { - /* Base tokens */ - /* Do not use base tokens directly as they don't carry any meaning and are used to set our base. Refer to Application tokens below. */ + /** Background Color **/ + --background-color-box: light-dark(var(--color-white), var(--color-gray-80)); + --background-color-critical: light-dark(var(--color-red-05), var(--color-red-80)); + --background-color-information: light-dark(var(--color-blue-05), var(--color-blue-80)); + --background-color-success: light-dark(var(--color-green-05), var(--color-green-80)); + --background-color-warning: light-dark(var(--color-yellow-05), var(--color-yellow-80)); + + /** Border **/ + --border-color-interactive-hover: var(--border-color-interactive); + --border-color-interactive-active: var(--border-color-interactive); + --border-color-interactive-disabled: var(--border-color-interactive); + --border-radius-circle: 9999px; + --border-radius-small: 4px; + --border-radius-medium: 8px; + --border-width: 1px; + + /** Box Shadow **/ + --box-shadow-10: 0 1px 4px var(--color-black-a10); + + /** Button **/ + --button-background-color-disabled: var(--button-background-color); + --button-background-color-destructive: light-dark(var(--color-red-50), var(--color-red-30)); + --button-background-color-destructive-hover: light-dark(var(--color-red-60), var(--color-red-10)); + --button-background-color-destructive-active: light-dark(var(--color-red-70), var(--color-red-05)); + --button-background-color-destructive-disabled: var(--button-background-color-destructive); + --button-background-color-ghost: transparent; + --button-background-color-ghost-hover: var(--button-background-color-hover); + --button-background-color-ghost-active: var(--button-background-color-active); + --button-background-color-ghost-disabled: var(--button-background-color-ghost); + --button-background-color-primary: var(--color-accent-primary); + --button-background-color-primary-hover: var(--color-accent-primary-hover); + --button-background-color-primary-active: var(--color-accent-primary-active); + --button-background-color-primary-disabled: var(--button-background-color-primary); + --button-border: var(--border-width) solid var(--button-border-color); + --button-border-color: transparent; + --button-border-color-hover: var(--button-border-color); + --button-border-color-active: var(--button-border-color); + --button-border-color-disabled: var(--button-border-color); + --button-border-color-destructive: transparent; + --button-border-color-destructive-hover: var(--button-border-color-destructive); + --button-border-color-destructive-active: var(--button-border-color-destructive); + --button-border-color-destructive-disabled: var(--button-border-color-destructive); + --button-border-color-ghost: var(--button-border-color); + --button-border-color-ghost-hover: var(--button-border-color-hover); + --button-border-color-ghost-active: var(--button-border-color-active); + --button-border-color-ghost-disabled: var(--button-border-color-disabled); + --button-border-color-primary: transparent; + --button-border-color-primary-hover: var(--button-border-color-primary); + --button-border-color-primary-active: var(--button-border-color-primary); + --button-border-color-primary-disabled: var(--button-border-color-primary); + --button-border-radius: var(--border-radius-small); + --button-font-size: var(--font-size-root); + --button-font-size-small: var(--font-size-small); + --button-font-weight: var(--font-weight-bold); + --button-min-height: var(--size-item-large); + --button-min-height-small: var(--size-item-medium); + --button-opacity-disabled: 0.5; + --button-padding: var(--space-xsmall) var(--space-large); + --button-padding-icon: 0; + --button-size-icon: var(--button-min-height); + --button-size-icon-small: var(--button-min-height-small); + --button-text-color-hover: var(--button-text-color); + --button-text-color-active: var(--button-text-color); + --button-text-color-disabled: var(--button-text-color); + --button-text-color-destructive: light-dark(var(--color-gray-05), var(--color-gray-100)); + --button-text-color-destructive-hover: var(--button-text-color-destructive); + --button-text-color-destructive-active: var(--button-text-color-destructive); + --button-text-color-destructive-disabled: var(--button-text-color-destructive); + --button-text-color-ghost: var(--button-text-color); + --button-text-color-ghost-hover: var(--button-text-color-hover); + --button-text-color-ghost-active: var(--button-text-color-active); + --button-text-color-ghost-disabled: var(--button-text-color-disabled); + --button-text-color-primary-hover: var(--button-text-color-primary); + --button-text-color-primary-active: var(--button-text-color-primary-hover); + --button-text-color-primary-disabled: var(--button-text-color-primary); + + /** Checkbox **/ + --checkbox-margin-inline: var(--space-small); + --checkbox-size: var(--size-item-small); /* TODO Bug 1876537: Make this em-based, probably? */ + /** Color **/ - --color-white: #ffffff; --color-black-a10: rgba(0, 0, 0, 0.1); --color-blue-05: #deeafc; --color-blue-30: #73a7f3; @@ -38,133 +118,46 @@ --color-red-30: #f37f98; --color-red-50: #d7264c; --color-red-60: #ac1e3d; - --color-red-70: #8A1831; + --color-red-70: #8a1831; --color-red-80: #690f22; + --color-white: #ffffff; --color-yellow-05: #ffebcd; --color-yellow-30: #e49c49; --color-yellow-50: #cd411e; --color-yellow-80: #5a3100; - /* Application tokens */ - /** Border **/ - --border-radius-circle: 9999px; - --border-radius-small: 4px; - --border-radius-medium: 8px; - --border-width: 1px; - --border-interactive-color-hover: var(--border-interactive-color); - --border-interactive-color-active: var(--border-interactive-color); - --border-interactive-color-disabled: var(--border-interactive-color); - - /** Box **/ - --box-background-color: light-dark(var(--color-white), var(--color-gray-80)); - --box-shadow-10: 0 1px 4px var(--color-black-a10); - - /** Color **/ - --color-background-information: light-dark(var(--color-blue-05), var(--color-blue-80)); - --color-background-success: light-dark(var(--color-green-05), var(--color-green-80)); - --color-background-warning: light-dark(var(--color-yellow-05), var(--color-yellow-80)); - --color-background-critical: light-dark(var(--color-red-05), var(--color-red-80)); - --color-error-outline: light-dark(var(--color-red-50), var(--color-red-20)); - - /** Font weight **/ - --font-weight: normal; - --font-weight-bold: 600; - - /** Focus outline **/ + /** Focus Outline **/ --focus-outline: var(--focus-outline-width) solid var(--focus-outline-color); --focus-outline-color: var(--color-accent-primary); --focus-outline-inset: calc(-1 * var(--focus-outline-width)); --focus-outline-offset: 2px; --focus-outline-width: 2px; + /** Font Weight **/ + --font-weight: normal; + --font-weight-bold: 600; + /** Icon **/ --icon-color: light-dark(var(--color-gray-70), var(--color-gray-05)); + --icon-color-critical: light-dark(var(--color-red-50), var(--color-red-30)); --icon-color-information: light-dark(var(--color-blue-50), var(--color-blue-30)); --icon-color-success: light-dark(var(--color-green-50), var(--color-green-30)); --icon-color-warning: light-dark(var(--color-yellow-50), var(--color-yellow-30)); - --icon-color-critical: light-dark(var(--color-red-50), var(--color-red-30)); --icon-size-default: var(--size-item-small); - /** Input **/ - /*** Button ***/ - --button-border: var(--border-width) solid var(--button-border-color); - --button-border-radius: var(--border-radius-small); - --button-font-weight: var(--font-weight-bold); - --button-font-size: var(--font-size-root); - --button-font-size-small: var(--font-size-small); - --button-min-height: var(--size-item-large); - --button-min-height-small: var(--size-item-medium); - --button-size-icon: var(--button-min-height); - --button-size-icon-small: var(--button-min-height-small); - --button-padding: var(--space-xsmall) var(--space-large); - --button-padding-icon: 0; - - --button-text-color: var(--text-color); - --button-text-color-hover: var(--button-text-color); - --button-text-color-active: var(--button-text-color); - --button-text-color-disabled: var(--button-text-color); - --button-border-color: transparent; - --button-border-color-hover: var(--button-border-color); - --button-border-color-active: var(--button-border-color); - --button-border-color-disabled: var(--button-border-color); - --button-background-color-disabled: var(--button-background-color); - --button-opacity-disabled: 0.5; - - --button-background-color-primary: var(--color-accent-primary); - --button-background-color-primary-hover: var(--color-accent-primary-hover); - --button-background-color-primary-active: var(--color-accent-primary-active); - --button-background-color-primary-disabled: var(--button-background-color-primary); - --button-text-color-primary-hover: var(--button-text-color-primary); - --button-text-color-primary-active: var(--button-text-color-primary-hover); - --button-text-color-primary-disabled: var(--button-text-color-primary); - --button-border-color-primary: transparent; - --button-border-color-primary-hover: var(--button-border-color-primary); - --button-border-color-primary-active: var(--button-border-color-primary); - --button-border-color-primary-disabled: var(--button-border-color-primary); - - --button-background-color-destructive: light-dark(var(--color-red-50), var(--color-red-30)); - --button-background-color-destructive-hover: light-dark(var(--color-red-60), var(--color-red-10)); - --button-background-color-destructive-active: light-dark(var(--color-red-70), var(--color-red-05)); - --button-background-color-destructive-disabled: var(--button-background-color-destructive); - --button-text-color-destructive: light-dark(var(--color-gray-05), var(--color-gray-100)); - --button-text-color-destructive-hover: var(--button-text-color-destructive); - --button-text-color-destructive-active: var(--button-text-color-destructive); - --button-text-color-destructive-disabled: var(--button-text-color-destructive); - --button-border-color-destructive: transparent; - --button-border-color-destructive-hover: var(--button-border-color-destructive); - --button-border-color-destructive-active: var(--button-border-color-destructive); - --button-border-color-destructive-disabled: var(--button-border-color-destructive); - - --button-background-color-ghost: transparent; - --button-background-color-ghost-hover: var(--button-background-color-hover); - --button-background-color-ghost-active: var(--button-background-color-active); - --button-background-color-ghost-disabled: var(--button-background-color-ghost); - --button-text-color-ghost: var(--button-text-color); - --button-text-color-ghost-hover: var(--button-text-color-hover); - --button-text-color-ghost-active: var(--button-text-color-ghost-active); - --button-text-color-ghost-disabled: var(--button-text-color); - --button-border-color-ghost: var(--button-border-color); - --button-border-color-ghost-hover: var(--button-border-color-hover); - --button-border-color-ghost-active: var(--button-border-color-active); - --button-border-color-ghost-disabled: var(--button-border-color); - - /*** Checkbox ***/ - --checkbox-margin-inline: var(--space-small); - /* TODO Bug 1876537: Make this em-based, probably? */ - --checkbox-size: var(--size-item-small); - - /*** Text ***/ + /** Input - Text **/ --input-text-min-height: var(--button-min-height); /** Link **/ - /* Not using --focus-outline-offset for links because that's intended for - elements with a background, and we only want a slight offset here while not - overlapping adjacent text. */ + /** + * Not using --force-outline-offset for links because that's intended for + * elements with a background, and we only want a slight offset here while + * not overlapping adjacent text + */ --link-focus-outline-offset: 1px; - /** Text **/ - --text-color-deemphasized: color-mix(in srgb, currentColor 69%, transparent); - --text-color-error: light-dark(var(--color-red-50), var(--color-red-20)); + /** Outline Color **/ + --outline-color-error: light-dark(var(--color-red-50), var(--color-red-20)); /** Size **/ --size-item-small: 16px; @@ -179,6 +172,10 @@ --space-large: calc(4 * var(--space-xsmall)); --space-xlarge: calc(6 * var(--space-xsmall)); --space-xxlarge: calc(8 * var(--space-xsmall)); + + /** Text **/ + --text-color-deemphasized: color-mix(in srgb, currentColor 69%, transparent); + --text-color-error: light-dark(var(--color-red-50), var(--color-red-20)); } } @@ -188,19 +185,31 @@ @media (prefers-contrast) { :root, :host(.anonymous-content-host) { - /* Border */ - --border-color: var(--text-color); - --border-interactive-color: AccentColor; - --border-interactive-color-hover: ButtonText; - --border-interactive-color-active: AccentColor; - --border-interactive-color-disabled: GrayText; + /** Attention Dot **/ + --attention-dot-color: AccentColor; - /** Box **/ - --box-background-color: var(--color-canvas); + /** Background Color **/ + --background-color-box: var(--background-color-canvas); + --background-color-canvas: Canvas; + --background-color-critical: var(--background-color-canvas); + --background-color-information: var(--background-color-canvas); + --background-color-success: var(--background-color-canvas); + --background-color-warning: var(--background-color-canvas); - /* Button */ - --button-border-color: var(--button-text-color); + /** Border **/ + --border-color: var(--text-color); + --border-color-interactive: var(--text-color); + + /** Button **/ --button-background-color-ghost: var(--button-background-color); + --button-border-color: var(--button-text-color); + + /** Icon **/ + --icon-color: var(--text-color); + --icon-color-critical: var(--icon-color); + --icon-color-information: var(--icon-color); + --icon-color-success: var(--icon-color); + --icon-color-warning: var(--icon-color); /** Link **/ --link-color: LinkText; @@ -208,93 +217,68 @@ --link-color-active: ActiveText; --link-color-visited: var(--link-color); - /** Color **/ - --color-canvas: Canvas; - --color-background-information: var(--color-canvas); - --color-background-success: var(--color-canvas); - --color-background-warning: var(--color-canvas); - --color-background-critical: var(--color-canvas); - --color-error-outline: var(--border-color); - - /** Icon **/ - --icon-color: var(--text-color); - --icon-color-information: var(--icon-color); - --icon-color-success: var(--icon-color); - --icon-color-warning: var(--icon-color); - --icon-color-critical: var(--icon-color); + /** Outline Color **/ + --outline-color-error: var(--border-color); /** Text **/ --text-color: CanvasText; - --text-color-error: inherit; --text-color-deemphasized: inherit; + --text-color-error: inherit; } } } /* Bug 1879900: Can't nest media queries inside of :host, :root selector until Bug 1879349 lands */ -/* NOTE: These do not apply in the browser chrome (bug 1878919). */ @layer tokens-forced-colors { @media (forced-colors) { :root, :host(.anonymous-content-host) { /** Border **/ - --border-interactive-color: ButtonText; - --border-interactive-color-hover: SelectedItem; - --border-interactive-color-active: ButtonText; - --border-interactive-color-disabled: GrayText; + --border-color-interactive: ButtonText; + --border-color-interactive-hover: SelectedItem; + --border-color-interactive-active: ButtonText; + --border-color-interactive-disabled: GrayText; /** Button **/ - --button-border-color: var(--border-interactive-color); - --button-border-color-hover: var(--border-interactive-color-hover); - --button-border-color-active: var(--border-interactive-color-active); - --button-border-color-disabled: var(--border-interactive-color-disabled); - --button-background-color: ButtonFace; + --button-background-color: ButtonFace; /* TODO Bug 1821203 - Gray use needs to be consolidated */ --button-background-color-hover: SelectedItemText; --button-background-color-active: SelectedItemText; --button-background-color-disabled: ButtonFace; - --button-text-color: ButtonText; - --button-text-color-hover: SelectedItem; - --button-text-color-active: SelectedItem; - --button-text-color-disabled: GrayText; - --button-opacity-disabled: 1; - + --button-background-color-destructive: var(--button-background-color-primary); + --button-background-color-destructive-hover: var(--button-background-color-primary-hover); + --button-background-color-destructive-active: var(--button-background-color-primary-active); + --button-background-color-destructive-disabled: var(--button-background-color-primary-disabled); + --button-background-color-ghost: var(--button-background-color); + --button-background-color-ghost-disabled: var(--button-background-color-disabled); --button-background-color-primary-disabled: var(--button-text-color-disabled); - --button-border-color-primary: ButtonFace; - --button-border-color-primary-hover: SelectedItemText; - --button-border-color-primary-active: ButtonText; - --button-text-color-primary: ButtonFace; - --button-text-color-primary-hover: SelectedItemText; - + --button-border-color: var(--border-color-interactive); + --button-border-color-hover: var(--border-color-interactive-hover); + --button-border-color-active: var(--border-color-interactive-active); + --button-border-color-disabled: var(--border-color-interactive-disabled); --button-border-color-destructive: var(--button-border-color-primary); --button-border-color-destructive-hover: var(--button-border-color-primary-hover); --button-border-color-destructive-active: var(--button-border-color-primary-active); --button-border-color-destructive-disabled: var(--button-border-color-primary-disabled); - --button-background-color-destructive: var(--button-background-color-primary); - --button-background-color-destructive-hover: var(--button-background-color-primary-hover); - --button-background-color-destructive-active: var(--button-background-color-primary-active); - --button-background-color-destructive-disabled: var(--button-background-color-primary-disabled); + --button-border-color-primary: ButtonFace; + --button-border-color-primary-hover: SelectedItemText; + --button-border-color-primary-active: ButtonText; + --button-opacity-disabled: 1; + --button-text-color: ButtonText; + --button-text-color-hover: SelectedItem; + --button-text-color-active: SelectedItem; + --button-text-color-disabled: GrayText; --button-text-color-destructive: var(--button-text-color-primary); --button-text-color-destructive-hover: var(--button-text-color-primary-hover); --button-text-color-destructive-active: var(--button-text-color-primary-active); --button-text-color-destructive-disabled: var(--button-text-color-primary-disabled); - - --button-border-color-ghost: var(--button-border-color); - --button-border-color-ghost-hover: var(--button-border-color-hover); - --button-border-color-ghost-active: var(--button-border-color-active); - --button-border-color-ghost-disabled: var(--button-border-color-disabled); - --button-background-color-ghost: var(--button-background-color); - --button-background-color-ghost-disabled: var(--button-background-color-disabled); - --button-text-color-ghost: var(--button-text-color); - --button-text-color-ghost-hover: var(--button-text-color-hover); - --button-text-color-ghost-active: var(--button-text-color-active); - --button-text-color-ghost-disabled: var(--button-text-color-disabled); + --button-text-color-primary: ButtonFace; + --button-text-color-primary-hover: SelectedItemText; /** Color **/ --color-accent-primary: ButtonText; --color-accent-primary-hover: SelectedItem; --color-accent-primary-active: var(--color-accent-primary-hover); - --color-error-outline: var(--border-color); } } } diff --git a/toolkit/themes/shared/design-system/tokens-storybook.mjs b/toolkit/themes/shared/design-system/tokens-storybook.mjs new file mode 100644 index 0000000000..fd2ea174c6 --- /dev/null +++ b/toolkit/themes/shared/design-system/tokens-storybook.mjs @@ -0,0 +1,1172 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* DO NOT EDIT this file directly, instead modify design-tokens.json + * and run `npm run build` to see your changes. */ + +export const storybookTables = { + "attention-dot": [ + { + value: { + platform: { default: "AccentColor" }, + brand: { + light: "#2ac3a2", + dark: "#54ffbd", + default: "light-dark(#2ac3a2, #54ffbd)", + }, + prefersContrast: "AccentColor", + }, + name: "--attention-dot-color", + }, + ], + "background-color": [ + { + value: { + light: "var(--color-white)", + dark: "var(--color-gray-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-white), var(--color-gray-80))", + }, + name: "--background-color-box", + }, + { + value: { + prefersContrast: "Canvas", + brand: { + light: "var(--color-white)", + dark: "var(--color-gray-90)", + default: "light-dark(var(--color-white), var(--color-gray-90))", + }, + platform: { default: "Canvas" }, + }, + name: "--background-color-canvas", + }, + { + value: { + light: "var(--color-red-05)", + dark: "var(--color-red-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-red-05), var(--color-red-80))", + }, + name: "--background-color-critical", + }, + { + value: { + light: "var(--color-blue-05)", + dark: "var(--color-blue-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-blue-05), var(--color-blue-80))", + }, + name: "--background-color-information", + }, + { + value: { + light: "var(--color-green-05)", + dark: "var(--color-green-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-green-05), var(--color-green-80))", + }, + name: "--background-color-success", + }, + { + value: { + light: "var(--color-yellow-05)", + dark: "var(--color-yellow-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-yellow-05), var(--color-yellow-80))", + }, + name: "--background-color-warning", + }, + { + value: { + forcedColors: "ButtonFace", + brand: { default: "color-mix(in srgb, currentColor 7%, transparent)" }, + platform: { default: "var(--button-bgcolor)" }, + }, + name: "--button-background-color", + }, + { + value: { + forcedColors: "SelectedItemText", + brand: { default: "color-mix(in srgb, currentColor 14%, transparent)" }, + platform: { default: "var(--button-hover-bgcolor)" }, + }, + name: "--button-background-color-hover", + }, + { + value: { + forcedColors: "SelectedItemText", + brand: { default: "color-mix(in srgb, currentColor 21%, transparent)" }, + platform: { default: "var(--button-active-bgcolor)" }, + }, + name: "--button-background-color-active", + }, + { + value: { + default: "var(--button-background-color)", + forcedColors: "ButtonFace", + }, + name: "--button-background-color-disabled", + }, + { + value: "var(--color-accent-primary)", + name: "--button-background-color-primary", + }, + { + value: "var(--color-accent-primary-hover)", + name: "--button-background-color-primary-hover", + }, + { + value: "var(--color-accent-primary-active)", + name: "--button-background-color-primary-active", + }, + { + value: { + default: "var(--button-background-color-primary)", + forcedColors: "var(--button-text-color-disabled)", + }, + name: "--button-background-color-primary-disabled", + }, + { + value: { + light: "var(--color-red-50)", + dark: "var(--color-red-30)", + forcedColors: "var(--button-background-color-primary)", + default: "light-dark(var(--color-red-50), var(--color-red-30))", + }, + name: "--button-background-color-destructive", + }, + { + value: { + light: "var(--color-red-70)", + dark: "var(--color-red-05)", + forcedColors: "var(--button-background-color-primary-active)", + default: "light-dark(var(--color-red-70), var(--color-red-05))", + }, + name: "--button-background-color-destructive-active", + }, + { + value: { + default: "var(--button-background-color-destructive)", + forcedColors: "var(--button-background-color-primary-disabled)", + }, + name: "--button-background-color-destructive-disabled", + }, + { + value: { + light: "var(--color-red-60)", + dark: "var(--color-red-10)", + forcedColors: "var(--button-background-color-primary-hover)", + default: "light-dark(var(--color-red-60), var(--color-red-10))", + }, + name: "--button-background-color-destructive-hover", + }, + { + value: { + default: "transparent", + prefersContrast: "var(--button-background-color)", + forcedColors: "var(--button-background-color)", + }, + name: "--button-background-color-ghost", + }, + { + value: "var(--button-background-color-active)", + name: "--button-background-color-ghost-active", + }, + { + value: { + default: "var(--button-background-color-ghost)", + forcedColors: "var(--button-background-color-disabled)", + }, + name: "--button-background-color-ghost-disabled", + }, + { + value: "var(--button-background-color-hover)", + name: "--button-background-color-ghost-hover", + }, + ], + "border-color": [ + { value: { prefersContrast: "var(--text-color)" }, name: "--border-color" }, + { + value: { + prefersContrast: "var(--text-color)", + forcedColors: "ButtonText", + brand: { + light: "var(--color-gray-60)", + dark: "var(--color-gray-50)", + default: "light-dark(var(--color-gray-60), var(--color-gray-50))", + }, + platform: { + default: "color-mix(in srgb, currentColor 15%, var(--color-gray-60))", + }, + }, + name: "--border-color-interactive", + }, + { + value: { + default: "var(--border-color-interactive)", + forcedColors: "SelectedItem", + }, + name: "--border-color-interactive-hover", + }, + { + value: { + default: "var(--border-color-interactive)", + forcedColors: "ButtonText", + }, + name: "--border-color-interactive-active", + }, + { + value: { + default: "var(--border-color-interactive)", + forcedColors: "GrayText", + }, + name: "--border-color-interactive-disabled", + }, + { + value: { + default: "transparent", + prefersContrast: "var(--button-text-color)", + forcedColors: "var(--border-color-interactive)", + }, + name: "--button-border-color", + }, + { + value: { + default: "var(--button-border-color)", + forcedColors: "var(--border-color-interactive-active)", + }, + name: "--button-border-color-active", + }, + { + value: { + default: "transparent", + forcedColors: "var(--button-border-color-primary)", + }, + name: "--button-border-color-destructive", + }, + { + value: { + default: "var(--button-border-color-destructive)", + forcedColors: "var(--button-border-color-primary-active)", + }, + name: "--button-border-color-destructive-active", + }, + { + value: { + default: "var(--button-border-color-destructive)", + forcedColors: "var(--button-border-color-primary-disabled)", + }, + name: "--button-border-color-destructive-disabled", + }, + { + value: { + default: "var(--button-border-color-destructive)", + forcedColors: "var(--button-border-color-primary-hover)", + }, + name: "--button-border-color-destructive-hover", + }, + { + value: { + default: "var(--button-border-color)", + forcedColors: "var(--border-color-interactive-disabled)", + }, + name: "--button-border-color-disabled", + }, + { + value: { default: "var(--button-border-color)" }, + name: "--button-border-color-ghost", + }, + { + value: { default: "var(--button-border-color-active)" }, + name: "--button-border-color-ghost-active", + }, + { + value: { default: "var(--button-border-color-disabled)" }, + name: "--button-border-color-ghost-disabled", + }, + { + value: { default: "var(--button-border-color-hover)" }, + name: "--button-border-color-ghost-hover", + }, + { + value: { + default: "var(--button-border-color)", + forcedColors: "var(--border-color-interactive-hover)", + }, + name: "--button-border-color-hover", + }, + { + value: { default: "transparent", forcedColors: "ButtonFace" }, + name: "--button-border-color-primary", + }, + { + value: { + default: "var(--button-border-color-primary)", + forcedColors: "ButtonText", + }, + name: "--button-border-color-primary-active", + }, + { + value: "var(--button-border-color-primary)", + name: "--button-border-color-primary-disabled", + }, + { + value: { + default: "var(--button-border-color-primary)", + forcedColors: "SelectedItemText", + }, + name: "--button-border-color-primary-hover", + }, + ], + "border-radius": [ + { value: "9999px", name: "--border-radius-circle" }, + { value: "4px", name: "--border-radius-small" }, + { value: "8px", name: "--border-radius-medium" }, + { value: "var(--border-radius-small)", name: "--button-border-radius" }, + ], + "border-width": [{ value: "1px", name: "--border-width" }], + "box-shadow": [ + { value: "0 1px 4px var(--color-black-a10)", name: "--box-shadow-10" }, + ], + border: [ + { + value: "var(--border-width) solid var(--button-border-color)", + name: "--button-border", + }, + ], + "font-size": [ + { value: "var(--font-size-root)", name: "--button-font-size" }, + { value: "var(--font-size-small)", name: "--button-font-size-small" }, + { + value: { brand: { default: "15px" }, platform: { default: "unset" } }, + name: "--font-size-root", + }, + { + value: { brand: { default: "0.867rem" }, platform: { default: "unset" } }, + name: "--font-size-small", + }, + { + value: { brand: { default: "1.133rem" }, platform: { default: "unset" } }, + name: "--font-size-large", + }, + { + value: { brand: { default: "1.467rem" }, platform: { default: "unset" } }, + name: "--font-size-xlarge", + }, + { + value: { brand: { default: "1.6rem" }, platform: { default: "unset" } }, + name: "--font-size-xxlarge", + }, + ], + "font-weight": [ + { value: "var(--font-weight-bold)", name: "--button-font-weight" }, + { value: "normal", name: "--font-weight" }, + { value: 600, name: "--font-weight-bold" }, + ], + "min-height": [ + { value: "var(--size-item-large)", name: "--button-min-height" }, + { value: "var(--size-item-medium)", name: "--button-min-height-small" }, + { value: "var(--button-min-height)", name: "--input-text-min-height" }, + ], + opacity: [ + { + value: { default: 0.5, forcedColors: 1 }, + name: "--button-opacity-disabled", + }, + ], + padding: [ + { + value: "var(--space-xsmall) var(--space-large)", + name: "--button-padding", + }, + { value: 0, name: "--button-padding-icon" }, + ], + size: [ + { value: "var(--button-min-height)", name: "--button-size-icon" }, + { + value: "var(--button-min-height-small)", + name: "--button-size-icon-small", + }, + { value: "var(--size-item-small)", name: "--checkbox-size" }, + { value: "16px", name: "--size-item-small" }, + { value: "28px", name: "--size-item-medium" }, + { value: "32px", name: "--size-item-large" }, + ], + "text-color": [ + { + value: { + forcedColors: "ButtonText", + brand: { + light: "var(--color-gray-100)", + dark: "var(--color-gray-05)", + default: "light-dark(var(--color-gray-100), var(--color-gray-05))", + }, + platform: { default: "var(--button-color)" }, + }, + name: "--button-text-color", + }, + { + value: { + default: "var(--button-text-color)", + forcedColors: "SelectedItem", + }, + name: "--button-text-color-active", + }, + { + value: { + light: "var(--color-gray-05)", + dark: "var(--color-gray-100)", + forcedColors: "var(--button-text-color-primary)", + default: "light-dark(var(--color-gray-05), var(--color-gray-100))", + }, + name: "--button-text-color-destructive", + }, + { + value: { + default: "var(--button-text-color-destructive)", + forcedColors: "var(--button-text-color-primary-active)", + }, + name: "--button-text-color-destructive-active", + }, + { + value: { + default: "var(--button-text-color-destructive)", + forcedColors: "var(--button-text-color-primary-disabled)", + }, + name: "--button-text-color-destructive-disabled", + }, + { + value: { + default: "var(--button-text-color-destructive)", + forcedColors: "var(--button-text-color-primary-hover)", + }, + name: "--button-text-color-destructive-hover", + }, + { + value: { default: "var(--button-text-color)", forcedColors: "GrayText" }, + name: "--button-text-color-disabled", + }, + { + value: { default: "var(--button-text-color)" }, + name: "--button-text-color-ghost", + }, + { + value: { default: "var(--button-text-color-active)" }, + name: "--button-text-color-ghost-active", + }, + { + value: { default: "var(--button-text-color-disabled)" }, + name: "--button-text-color-ghost-disabled", + }, + { + value: { default: "var(--button-text-color-hover)" }, + name: "--button-text-color-ghost-hover", + }, + { + value: { + default: "var(--button-text-color)", + forcedColors: "SelectedItem", + }, + name: "--button-text-color-hover", + }, + { + value: { + forcedColors: "ButtonFace", + brand: { + light: "var(--color-gray-05)", + dark: "var(--color-gray-100)", + default: "light-dark(var(--color-gray-05), var(--color-gray-100))", + }, + platform: { default: "var(--button-primary-color)" }, + }, + name: "--button-text-color-primary", + }, + { + value: "var(--button-text-color-primary-hover)", + name: "--button-text-color-primary-active", + }, + { + value: "var(--button-text-color-primary)", + name: "--button-text-color-primary-disabled", + }, + { + value: { + default: "var(--button-text-color-primary)", + forcedColors: "SelectedItemText", + }, + name: "--button-text-color-primary-hover", + }, + { + value: { + prefersContrast: "CanvasText", + brand: { + light: "var(--color-gray-100)", + dark: "var(--color-gray-05)", + default: "light-dark(var(--color-gray-100), var(--color-gray-05))", + }, + platform: { default: "currentColor" }, + }, + name: "--text-color", + }, + { + value: { + default: "color-mix(in srgb, currentColor 69%, transparent)", + prefersContrast: "inherit", + }, + name: "--text-color-deemphasized", + }, + { + value: { + light: "var(--color-red-50)", + dark: "var(--color-red-20)", + prefersContrast: "inherit", + default: "light-dark(var(--color-red-50), var(--color-red-20))", + }, + name: "--text-color-error", + }, + ], + margin: [{ value: "var(--space-small)", name: "--checkbox-margin-inline" }], + color: [ + { value: "rgba(0, 0, 0, 0.1)", name: "--color-black-a10" }, + { value: "#73a7f3", name: "--color-blue-30" }, + { value: "#0060df", name: "--color-blue-50" }, + { value: "#0250bb", name: "--color-blue-60" }, + { value: "#054096", name: "--color-blue-70" }, + { value: "#003070", name: "--color-blue-80" }, + { value: "#deeafc", name: "--color-blue-05" }, + { value: "#aaf2ff", name: "--color-cyan-20" }, + { value: "#80ebff", name: "--color-cyan-30" }, + { value: "#00ddff", name: "--color-cyan-50" }, + { value: "#bfbfc9", name: "--color-gray-50" }, + { value: "#8f8f9d", name: "--color-gray-60" }, + { value: "#5b5b66", name: "--color-gray-70" }, + { value: "#23222b", name: "--color-gray-80" }, + { value: "#1c1b22", name: "--color-gray-90" }, + { value: "#15141a", name: "--color-gray-100" }, + { value: "#fbfbfe", name: "--color-gray-05" }, + { value: "#4dbc87", name: "--color-green-30" }, + { value: "#017a40", name: "--color-green-50" }, + { value: "#004725", name: "--color-green-80" }, + { value: "#d8eedc", name: "--color-green-05" }, + { value: "#ffbdc5", name: "--color-red-10" }, + { value: "#ff9aa2", name: "--color-red-20" }, + { value: "#f37f98", name: "--color-red-30" }, + { value: "#d7264c", name: "--color-red-50" }, + { value: "#ac1e3d", name: "--color-red-60" }, + { value: "#8a1831", name: "--color-red-70" }, + { value: "#690f22", name: "--color-red-80" }, + { value: "#ffe8e8", name: "--color-red-05" }, + { value: "#e49c49", name: "--color-yellow-30" }, + { value: "#cd411e", name: "--color-yellow-50" }, + { value: "#5a3100", name: "--color-yellow-80" }, + { value: "#ffebcd", name: "--color-yellow-05" }, + { value: "#ffffff", name: "--color-white" }, + { + value: { + forcedColors: "ButtonText", + brand: { + light: "var(--color-blue-50)", + dark: "var(--color-cyan-50)", + default: "light-dark(var(--color-blue-50), var(--color-cyan-50))", + }, + platform: { default: "var(--button-primary-bgcolor, AccentColor)" }, + }, + name: "--color-accent-primary", + }, + { + value: { + forcedColors: "SelectedItem", + brand: { + light: "var(--color-blue-60)", + dark: "var(--color-cyan-30)", + default: "light-dark(var(--color-blue-60), var(--color-cyan-30))", + }, + platform: { default: "var(--button-primary-hover-bgcolor)" }, + }, + name: "--color-accent-primary-hover", + }, + { + value: { + forcedColors: "var(--color-accent-primary-hover)", + brand: { + light: "var(--color-blue-70)", + dark: "var(--color-cyan-20)", + default: "light-dark(var(--color-blue-70), var(--color-cyan-20))", + }, + platform: { default: "var(--button-primary-active-bgcolor)" }, + }, + name: "--color-accent-primary-active", + }, + ], + outline: [ + { + value: "var(--focus-outline-width) solid var(--focus-outline-color)", + name: "--focus-outline", + }, + { value: "var(--color-accent-primary)", name: "--focus-outline-color" }, + { + value: "calc(-1 * var(--focus-outline-width))", + name: "--focus-outline-inset", + }, + { value: "2px", name: "--focus-outline-offset" }, + { value: "2px", name: "--focus-outline-width" }, + { + value: { + light: "var(--color-red-50)", + dark: "var(--color-red-20)", + prefersContrast: "var(--border-color)", + default: "light-dark(var(--color-red-50), var(--color-red-20))", + }, + name: "--outline-color-error", + }, + ], + "icon-color": [ + { + value: { + light: "var(--color-gray-70)", + dark: "var(--color-gray-05)", + prefersContrast: "var(--text-color)", + default: "light-dark(var(--color-gray-70), var(--color-gray-05))", + }, + name: "--icon-color", + }, + { + value: { + light: "var(--color-blue-50)", + dark: "var(--color-blue-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-blue-50), var(--color-blue-30))", + }, + name: "--icon-color-information", + }, + { + value: { + light: "var(--color-green-50)", + dark: "var(--color-green-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-green-50), var(--color-green-30))", + }, + name: "--icon-color-success", + }, + { + value: { + light: "var(--color-yellow-50)", + dark: "var(--color-yellow-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-yellow-50), var(--color-yellow-30))", + }, + name: "--icon-color-warning", + }, + { + value: { + light: "var(--color-red-50)", + dark: "var(--color-red-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-red-50), var(--color-red-30))", + }, + name: "--icon-color-critical", + }, + ], + "icon-size": [ + { value: "var(--size-item-small)", name: "--icon-size-default" }, + ], + link: [ + { + value: { + prefersContrast: "LinkText", + brand: { default: "var(--color-accent-primary)" }, + platform: { default: "LinkText" }, + }, + name: "--link-color", + }, + { + value: { + prefersContrast: "LinkText", + brand: { default: "var(--color-accent-primary-hover)" }, + platform: { default: "LinkText" }, + }, + name: "--link-color-hover", + }, + { + value: { + prefersContrast: "ActiveText", + brand: { default: "var(--color-accent-primary-active)" }, + platform: { default: "ActiveText" }, + }, + name: "--link-color-active", + }, + { + value: { + prefersContrast: "var(--link-color)", + brand: { default: "var(--link-color)" }, + platform: { default: "var(--link-color)" }, + }, + name: "--link-color-visited", + }, + { value: "1px", name: "--link-focus-outline-offset" }, + ], + space: [ + { value: "calc(0.5 * var(--space-xsmall))", name: "--space-xxsmall" }, + { value: "0.267rem", name: "--space-xsmall" }, + { value: "calc(2 * var(--space-xsmall))", name: "--space-small" }, + { value: "calc(3 * var(--space-xsmall))", name: "--space-medium" }, + { value: "calc(4 * var(--space-xsmall))", name: "--space-large" }, + { value: "calc(6 * var(--space-xsmall))", name: "--space-xlarge" }, + { value: "calc(8 * var(--space-xsmall))", name: "--space-xxlarge" }, + ], +}; + +export const variableLookupTable = { + "attention-dot-color": { + platform: { default: "AccentColor" }, + brand: { + light: "#2ac3a2", + dark: "#54ffbd", + default: "light-dark(#2ac3a2, #54ffbd)", + }, + prefersContrast: "AccentColor", + }, + "background-color-box": { + light: "var(--color-white)", + dark: "var(--color-gray-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-white), var(--color-gray-80))", + }, + "background-color-canvas": { + prefersContrast: "Canvas", + brand: { + light: "var(--color-white)", + dark: "var(--color-gray-90)", + default: "light-dark(var(--color-white), var(--color-gray-90))", + }, + platform: { default: "Canvas" }, + }, + "background-color-critical": { + light: "var(--color-red-05)", + dark: "var(--color-red-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-red-05), var(--color-red-80))", + }, + "background-color-information": { + light: "var(--color-blue-05)", + dark: "var(--color-blue-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-blue-05), var(--color-blue-80))", + }, + "background-color-success": { + light: "var(--color-green-05)", + dark: "var(--color-green-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-green-05), var(--color-green-80))", + }, + "background-color-warning": { + light: "var(--color-yellow-05)", + dark: "var(--color-yellow-80)", + prefersContrast: "var(--background-color-canvas)", + default: "light-dark(var(--color-yellow-05), var(--color-yellow-80))", + }, + "border-color": { prefersContrast: "var(--text-color)" }, + "border-color-interactive": { + prefersContrast: "var(--text-color)", + forcedColors: "ButtonText", + brand: { + light: "var(--color-gray-60)", + dark: "var(--color-gray-50)", + default: "light-dark(var(--color-gray-60), var(--color-gray-50))", + }, + platform: { + default: "color-mix(in srgb, currentColor 15%, var(--color-gray-60))", + }, + }, + "border-color-interactive-hover": { + default: "var(--border-color-interactive)", + forcedColors: "SelectedItem", + }, + "border-color-interactive-active": { + default: "var(--border-color-interactive)", + forcedColors: "ButtonText", + }, + "border-color-interactive-disabled": { + default: "var(--border-color-interactive)", + forcedColors: "GrayText", + }, + "border-radius-circle": "9999px", + "border-radius-small": "4px", + "border-radius-medium": "8px", + "border-width": "1px", + "box-shadow-10": "0 1px 4px var(--color-black-a10)", + "button-background-color": { + forcedColors: "ButtonFace", + brand: { default: "color-mix(in srgb, currentColor 7%, transparent)" }, + platform: { default: "var(--button-bgcolor)" }, + }, + "button-background-color-hover": { + forcedColors: "SelectedItemText", + brand: { default: "color-mix(in srgb, currentColor 14%, transparent)" }, + platform: { default: "var(--button-hover-bgcolor)" }, + }, + "button-background-color-active": { + forcedColors: "SelectedItemText", + brand: { default: "color-mix(in srgb, currentColor 21%, transparent)" }, + platform: { default: "var(--button-active-bgcolor)" }, + }, + "button-background-color-disabled": { + default: "var(--button-background-color)", + forcedColors: "ButtonFace", + }, + "button-background-color-primary": "var(--color-accent-primary)", + "button-background-color-primary-hover": "var(--color-accent-primary-hover)", + "button-background-color-primary-active": + "var(--color-accent-primary-active)", + "button-background-color-primary-disabled": { + default: "var(--button-background-color-primary)", + forcedColors: "var(--button-text-color-disabled)", + }, + "button-background-color-destructive": { + light: "var(--color-red-50)", + dark: "var(--color-red-30)", + forcedColors: "var(--button-background-color-primary)", + default: "light-dark(var(--color-red-50), var(--color-red-30))", + }, + "button-background-color-destructive-active": { + light: "var(--color-red-70)", + dark: "var(--color-red-05)", + forcedColors: "var(--button-background-color-primary-active)", + default: "light-dark(var(--color-red-70), var(--color-red-05))", + }, + "button-background-color-destructive-disabled": { + default: "var(--button-background-color-destructive)", + forcedColors: "var(--button-background-color-primary-disabled)", + }, + "button-background-color-destructive-hover": { + light: "var(--color-red-60)", + dark: "var(--color-red-10)", + forcedColors: "var(--button-background-color-primary-hover)", + default: "light-dark(var(--color-red-60), var(--color-red-10))", + }, + "button-background-color-ghost": { + default: "transparent", + prefersContrast: "var(--button-background-color)", + forcedColors: "var(--button-background-color)", + }, + "button-background-color-ghost-active": + "var(--button-background-color-active)", + "button-background-color-ghost-disabled": { + default: "var(--button-background-color-ghost)", + forcedColors: "var(--button-background-color-disabled)", + }, + "button-background-color-ghost-hover": "var(--button-background-color-hover)", + "button-border": "var(--border-width) solid var(--button-border-color)", + "button-border-color": { + default: "transparent", + prefersContrast: "var(--button-text-color)", + forcedColors: "var(--border-color-interactive)", + }, + "button-border-color-active": { + default: "var(--button-border-color)", + forcedColors: "var(--border-color-interactive-active)", + }, + "button-border-color-destructive": { + default: "transparent", + forcedColors: "var(--button-border-color-primary)", + }, + "button-border-color-destructive-active": { + default: "var(--button-border-color-destructive)", + forcedColors: "var(--button-border-color-primary-active)", + }, + "button-border-color-destructive-disabled": { + default: "var(--button-border-color-destructive)", + forcedColors: "var(--button-border-color-primary-disabled)", + }, + "button-border-color-destructive-hover": { + default: "var(--button-border-color-destructive)", + forcedColors: "var(--button-border-color-primary-hover)", + }, + "button-border-color-disabled": { + default: "var(--button-border-color)", + forcedColors: "var(--border-color-interactive-disabled)", + }, + "button-border-color-ghost": { default: "var(--button-border-color)" }, + "button-border-color-ghost-active": { + default: "var(--button-border-color-active)", + }, + "button-border-color-ghost-disabled": { + default: "var(--button-border-color-disabled)", + }, + "button-border-color-ghost-hover": { + default: "var(--button-border-color-hover)", + }, + "button-border-color-hover": { + default: "var(--button-border-color)", + forcedColors: "var(--border-color-interactive-hover)", + }, + "button-border-color-primary": { + default: "transparent", + forcedColors: "ButtonFace", + }, + "button-border-color-primary-active": { + default: "var(--button-border-color-primary)", + forcedColors: "ButtonText", + }, + "button-border-color-primary-disabled": "var(--button-border-color-primary)", + "button-border-color-primary-hover": { + default: "var(--button-border-color-primary)", + forcedColors: "SelectedItemText", + }, + "button-border-radius": "var(--border-radius-small)", + "button-font-size": "var(--font-size-root)", + "button-font-size-small": "var(--font-size-small)", + "button-font-weight": "var(--font-weight-bold)", + "button-min-height": "var(--size-item-large)", + "button-min-height-small": "var(--size-item-medium)", + "button-opacity-disabled": { default: 0.5, forcedColors: 1 }, + "button-padding": "var(--space-xsmall) var(--space-large)", + "button-padding-icon": 0, + "button-size-icon": "var(--button-min-height)", + "button-size-icon-small": "var(--button-min-height-small)", + "button-text-color": { + forcedColors: "ButtonText", + brand: { + light: "var(--color-gray-100)", + dark: "var(--color-gray-05)", + default: "light-dark(var(--color-gray-100), var(--color-gray-05))", + }, + platform: { default: "var(--button-color)" }, + }, + "button-text-color-active": { + default: "var(--button-text-color)", + forcedColors: "SelectedItem", + }, + "button-text-color-destructive": { + light: "var(--color-gray-05)", + dark: "var(--color-gray-100)", + forcedColors: "var(--button-text-color-primary)", + default: "light-dark(var(--color-gray-05), var(--color-gray-100))", + }, + "button-text-color-destructive-active": { + default: "var(--button-text-color-destructive)", + forcedColors: "var(--button-text-color-primary-active)", + }, + "button-text-color-destructive-disabled": { + default: "var(--button-text-color-destructive)", + forcedColors: "var(--button-text-color-primary-disabled)", + }, + "button-text-color-destructive-hover": { + default: "var(--button-text-color-destructive)", + forcedColors: "var(--button-text-color-primary-hover)", + }, + "button-text-color-disabled": { + default: "var(--button-text-color)", + forcedColors: "GrayText", + }, + "button-text-color-ghost": { default: "var(--button-text-color)" }, + "button-text-color-ghost-active": { + default: "var(--button-text-color-active)", + }, + "button-text-color-ghost-disabled": { + default: "var(--button-text-color-disabled)", + }, + "button-text-color-ghost-hover": { + default: "var(--button-text-color-hover)", + }, + "button-text-color-hover": { + default: "var(--button-text-color)", + forcedColors: "SelectedItem", + }, + "button-text-color-primary": { + forcedColors: "ButtonFace", + brand: { + light: "var(--color-gray-05)", + dark: "var(--color-gray-100)", + default: "light-dark(var(--color-gray-05), var(--color-gray-100))", + }, + platform: { default: "var(--button-primary-color)" }, + }, + "button-text-color-primary-active": "var(--button-text-color-primary-hover)", + "button-text-color-primary-disabled": "var(--button-text-color-primary)", + "button-text-color-primary-hover": { + default: "var(--button-text-color-primary)", + forcedColors: "SelectedItemText", + }, + "checkbox-margin-inline": "var(--space-small)", + "checkbox-size": "var(--size-item-small)", + "color-black-a10": "rgba(0, 0, 0, 0.1)", + "color-blue-30": "#73a7f3", + "color-blue-50": "#0060df", + "color-blue-60": "#0250bb", + "color-blue-70": "#054096", + "color-blue-80": "#003070", + "color-blue-05": "#deeafc", + "color-cyan-20": "#aaf2ff", + "color-cyan-30": "#80ebff", + "color-cyan-50": "#00ddff", + "color-gray-50": "#bfbfc9", + "color-gray-60": "#8f8f9d", + "color-gray-70": "#5b5b66", + "color-gray-80": "#23222b", + "color-gray-90": "#1c1b22", + "color-gray-100": "#15141a", + "color-gray-05": "#fbfbfe", + "color-green-30": "#4dbc87", + "color-green-50": "#017a40", + "color-green-80": "#004725", + "color-green-05": "#d8eedc", + "color-red-10": "#ffbdc5", + "color-red-20": "#ff9aa2", + "color-red-30": "#f37f98", + "color-red-50": "#d7264c", + "color-red-60": "#ac1e3d", + "color-red-70": "#8a1831", + "color-red-80": "#690f22", + "color-red-05": "#ffe8e8", + "color-yellow-30": "#e49c49", + "color-yellow-50": "#cd411e", + "color-yellow-80": "#5a3100", + "color-yellow-05": "#ffebcd", + "color-white": "#ffffff", + "color-accent-primary": { + forcedColors: "ButtonText", + brand: { + light: "var(--color-blue-50)", + dark: "var(--color-cyan-50)", + default: "light-dark(var(--color-blue-50), var(--color-cyan-50))", + }, + platform: { default: "var(--button-primary-bgcolor, AccentColor)" }, + }, + "color-accent-primary-hover": { + forcedColors: "SelectedItem", + brand: { + light: "var(--color-blue-60)", + dark: "var(--color-cyan-30)", + default: "light-dark(var(--color-blue-60), var(--color-cyan-30))", + }, + platform: { default: "var(--button-primary-hover-bgcolor)" }, + }, + "color-accent-primary-active": { + forcedColors: "var(--color-accent-primary-hover)", + brand: { + light: "var(--color-blue-70)", + dark: "var(--color-cyan-20)", + default: "light-dark(var(--color-blue-70), var(--color-cyan-20))", + }, + platform: { default: "var(--button-primary-active-bgcolor)" }, + }, + "focus-outline": + "var(--focus-outline-width) solid var(--focus-outline-color)", + "focus-outline-color": "var(--color-accent-primary)", + "focus-outline-inset": "calc(-1 * var(--focus-outline-width))", + "focus-outline-offset": "2px", + "focus-outline-width": "2px", + "font-size-root": { + brand: { default: "15px" }, + platform: { default: "unset" }, + }, + "font-size-small": { + brand: { default: "0.867rem" }, + platform: { default: "unset" }, + }, + "font-size-large": { + brand: { default: "1.133rem" }, + platform: { default: "unset" }, + }, + "font-size-xlarge": { + brand: { default: "1.467rem" }, + platform: { default: "unset" }, + }, + "font-size-xxlarge": { + brand: { default: "1.6rem" }, + platform: { default: "unset" }, + }, + "font-weight": "normal", + "font-weight-bold": 600, + "icon-color": { + light: "var(--color-gray-70)", + dark: "var(--color-gray-05)", + prefersContrast: "var(--text-color)", + default: "light-dark(var(--color-gray-70), var(--color-gray-05))", + }, + "icon-color-information": { + light: "var(--color-blue-50)", + dark: "var(--color-blue-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-blue-50), var(--color-blue-30))", + }, + "icon-color-success": { + light: "var(--color-green-50)", + dark: "var(--color-green-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-green-50), var(--color-green-30))", + }, + "icon-color-warning": { + light: "var(--color-yellow-50)", + dark: "var(--color-yellow-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-yellow-50), var(--color-yellow-30))", + }, + "icon-color-critical": { + light: "var(--color-red-50)", + dark: "var(--color-red-30)", + prefersContrast: "var(--icon-color)", + default: "light-dark(var(--color-red-50), var(--color-red-30))", + }, + "icon-size-default": "var(--size-item-small)", + "input-text-min-height": "var(--button-min-height)", + "link-color": { + prefersContrast: "LinkText", + brand: { default: "var(--color-accent-primary)" }, + platform: { default: "LinkText" }, + }, + "link-color-hover": { + prefersContrast: "LinkText", + brand: { default: "var(--color-accent-primary-hover)" }, + platform: { default: "LinkText" }, + }, + "link-color-active": { + prefersContrast: "ActiveText", + brand: { default: "var(--color-accent-primary-active)" }, + platform: { default: "ActiveText" }, + }, + "link-color-visited": { + prefersContrast: "var(--link-color)", + brand: { default: "var(--link-color)" }, + platform: { default: "var(--link-color)" }, + }, + "link-focus-outline-offset": "1px", + "outline-color-error": { + light: "var(--color-red-50)", + dark: "var(--color-red-20)", + prefersContrast: "var(--border-color)", + default: "light-dark(var(--color-red-50), var(--color-red-20))", + }, + "size-item-small": "16px", + "size-item-medium": "28px", + "size-item-large": "32px", + "space-xxsmall": "calc(0.5 * var(--space-xsmall))", + "space-xsmall": "0.267rem", + "space-small": "calc(2 * var(--space-xsmall))", + "space-medium": "calc(3 * var(--space-xsmall))", + "space-large": "calc(4 * var(--space-xsmall))", + "space-xlarge": "calc(6 * var(--space-xsmall))", + "space-xxlarge": "calc(8 * var(--space-xsmall))", + "text-color": { + prefersContrast: "CanvasText", + brand: { + light: "var(--color-gray-100)", + dark: "var(--color-gray-05)", + default: "light-dark(var(--color-gray-100), var(--color-gray-05))", + }, + platform: { default: "currentColor" }, + }, + "text-color-deemphasized": { + default: "color-mix(in srgb, currentColor 69%, transparent)", + prefersContrast: "inherit", + }, + "text-color-error": { + light: "var(--color-red-50)", + dark: "var(--color-red-20)", + prefersContrast: "inherit", + default: "light-dark(var(--color-red-50), var(--color-red-20))", + }, +}; diff --git a/toolkit/themes/shared/design-system/tokens-table.css b/toolkit/themes/shared/design-system/tokens-table.css new file mode 100644 index 0000000000..54f01a962a --- /dev/null +++ b/toolkit/themes/shared/design-system/tokens-table.css @@ -0,0 +1,333 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +:host { + --table-border: 2px solid var(--color-gray-50); + --table-background-color: color-mix(in srgb, + var(--color-gray-50) 20%, + transparent); +} + +/* Wrapper and filter styles */ + +.page-wrapper { + margin: 3rem; +} + +.filters-wrapper { + position: sticky; + top: 0; + z-index: 1; + display: flex; + align-items: center; + gap: var(--space-large); + background: var(--background-color-canvas); + padding: var(--space-small); +} + +fieldset { + flex: 1; + margin: 0; + padding-block: var(--space-small); + height: var(--input-text-min-height); + box-sizing: border-box; + gap: var(--space-medium); +} + +fieldset, +.search-wrapper { + border: 1px solid var(--border-color-interactive); + border-radius: var(--border-radius-small); + height: var(--input-text-min-height); + display: inline-flex; + align-items: center; +} + +.search-wrapper { + position: relative; +} + +.search-icon, +.clear-icon { + background-position: center; + background-repeat: no-repeat; + background-size: var(--size-item-small); + fill: currentColor; + -moz-context-properties: fill; + height: var(--size-item-small); + width: var(--size-item-small); + position: absolute; + inset-block: 0; + margin: auto 0; + padding: var(--space-xxsmall); +} + +.search-icon { + background-image: url(chrome://global/skin/icons/search-textbox.svg); + inset-inline-start: var(--space-small); +} + +.clear-icon { + background-image: url(chrome://global/skin/icons/close-12.svg); + inset-inline-end: var(--space-small); +} + +input[type="search"] { + border: none; + padding-block: var(--space-small); + padding-inline: var(--space-xxlarge); + border-radius: var(--border-radius-small); +} + +/* Table styles */ + +.table-wrapper { + box-sizing: border-box; + border-radius: var(--border-radius-small); + border: var(--table-border); + margin-block: 1em; + width: 100%; + + & > summary { + list-style-image: url("chrome://global/skin/icons/arrow-down.svg"); + display: flex; + align-items: center; + position: relative; + + &::before { + content: ""; + background-image: url("chrome://global/skin/icons/arrow-down.svg"); + background-position: center; + background-repeat: no-repeat; + height: 16px; + width: 16px; + position: absolute; + inset-inline-start: 8px; + } + } + + &[open] > summary::before { + background-image: url("chrome://global/skin/icons/arrow-up.svg"); + } +} + +.table-heading { + background-color: var(--table-background-color); + border-radius: var(---border-radius-small); + padding: 0 24px; + + & h3 { + margin: 0; + padding: 0.5em; + font-size: var(--font-size-large); + font-weight: var(--font-weight-bold); + text-transform: uppercase; + color: var(--text-color); + display: inline-block; + } +} + +table { + border-collapse: collapse; + border-spacing: 0; + text-align: left; + width: 100%; + table-layout: fixed; +} + +thead { + background-color: var(--table-background-color); + border-bottom: 1px solid var(--color-gray-50); + + & tr { + border-block-end: var(--table-border); + } +} + +tbody td { + vertical-align: top; + color: var(--color-gray-100); + border-bottom: var(--border-width) solid var(--color-gray-50); + + &.hcm-theme { + border-inline-start: var(--border-width) solid var(--color-gray-50); + } +} + +tr td:last-of-type { + border-inline-end: 0; +} + +tr td:first-of-type { + border-inline-start: 0; +} + +tbody tr:first-of-type td { + border-block-start: 2px solid var(--color-gray-50); +} + +tbody tr:last-of-type { + & td { + border-block-end: 0; + } + + & td:first-of-type { + border-radius: 0 0 0 2px; + } + + & td:last-of-type { + border-radius: 0 0 2px 0; + } +} + +th { + font-size: var(--font-size-small); + text-transform: uppercase; + font-weight: var(--font-weight); + text-align: center; +} + +tr td, +tr th { + padding: 8px; +} + +td { + background: var(--color-white); + text-align: center; +} + +th:first-of-type, +td:first-of-type { + text-align: start; +} + +.light-theme { + background: var(--color-white); + color: var(--color-gray-100); +} + +.dark-theme { + background-color: var(--color-gray-90); + color: var(--color-gray-05); +} + +.hcm-theme { + background-color: #000000; + color: #ffffff; +} + +.preview-wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + height: 100%; +} + +.value { + width: 100%; + margin: 0; +} + +/* Preview cell styles */ + +/** Default **/ + +.default-preview { + height: 50px; + width: 100px; + background-color: var(--button-background-color); + border: 1px solid var(--color-gray-60); + border-radius: 4px; +} + +/** Outline **/ + +.outline-preview { + height: 50px; + width: 100px; + background-color: color-mix(in srgb, currentColor 20%, transparent); + outline: 2px solid var(--color-gray-60); +} + +/** Font **/ + +.text-wrapper { + backdrop-filter: contrast(0.4); + padding: var(--space-small); + width: 100%; +} + +/** Icon **/ + +.icon-preview { + background-color: var(--color-gray-60); + height: var(--size-item-large); + width: var(--size-item-large); + /* FIXME: our icons don't seem to work when used as a mask */ + mask: url(https://upload.wikimedia.org/wikipedia/commons/c/c4/Globe_icon.svg) no-repeat center / contain; +} + +/** Link **/ + +.link-preview { + text-decoration: underline; + + &.outline { + outline: 2px solid var(--color-blue-50); + outline-offset: var(--link-focus-outline-offset); + } +} + +/** Space and size **/ + +.space-size-preview { + display: flex; + height: 50px; + width: 75%; + flex-wrap: wrap; + align-items: center; + justify-content: center; + + & .item { + height: 50%; + width: 40%; + background-color: var(--color-blue-60); + border-radius: var(--border-radius-small); + } +} + +.space-size-background { + background-color: var(--color-blue-05); + background-image: linear-gradient(135deg, + var(--color-blue-30) 10%, + #0000 0, + #0000 50%, + var(--color-blue-30) 0, + var(--color-blue-30) 60%, + #0000 0, + #0000); + background-size: 8px 8px; + border-radius: var(--border-radius-small); + border: var(--border-width) solid var(--color-gray-50); +} + +/** Padding **/ + +.padding-item { + min-height: calc(1.5 * var(--size-item-large)); + width: calc(3 * var(--size-item-large)); + border-radius: var(--border-radius-small); + display: flex; + justify-content: center; + align-items: center; + + &.inner { + background-color: var(--color-blue-60); + opacity: 0.5; + border-radius: 3px; + } +} diff --git a/toolkit/themes/shared/design-system/tokens-table.stories.mjs b/toolkit/themes/shared/design-system/tokens-table.stories.mjs new file mode 100644 index 0000000000..8e55d85d7c --- /dev/null +++ b/toolkit/themes/shared/design-system/tokens-table.stories.mjs @@ -0,0 +1,476 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { + html, + LitElement, + classMap, +} from "chrome://global/content/vendor/lit.all.mjs"; +import { storybookTables, variableLookupTable } from "./tokens-storybook.mjs"; +import styles from "./tokens-table.css"; + +export default { + title: "Docs/Tokens Table", + parameters: { + options: { + showPanel: false, + }, + }, +}; + +const HCM_MAP = { + ActiveText: "#8080FF", + ButtonBorder: "#000000", + ButtonFace: "#000000", + ButtonText: "#FFEE32", + Canvas: "#000000", + CanvasText: "#ffffff", + Field: "#000000", + FieldText: "#ffffff", + GrayText: "#A6A6A6", + Highlight: "#D6B4FD", + HighlightText: "#2B2B2B", + LinkText: "#8080FF", + Mark: "#000000", + MarkText: "#000000", + SelectedItem: "#D6B4FD", + SelectedItemText: "#2B2B2B", + AccentColor: "8080FF", + AccentColorText: "#2B2B2B", + VisitedText: "#8080FF", +}; + +const THEMED_TABLES = [ + "attention-dot", + "background-color", + "border", + "border-color", + "opacity", + "text-color", + "color", + "outline", + "icon-color", + "link", +]; + +/** + * + */ +class TablesPage extends LitElement { + #query = ""; + + static properties = { + surface: { type: String, state: true }, + tokensData: { type: Object, state: true }, + filteredTokens: { type: Object, state: true }, + }; + + constructor() { + super(); + this.surface = "brand"; + this.tokensData = storybookTables; + } + + handleSurfaceChange(e) { + this.surface = e.target.value; + } + + handleInput(e) { + this.#query = e.originalTarget.value.trim().toLowerCase(); + e.preventDefault(); + this.handleSearch(); + } + + debounce(fn, delayMs = 300) { + let timeout; + return function () { + clearTimeout(timeout); + timeout = setTimeout(() => { + fn.apply(this, arguments); + }, delayMs); + }; + } + + handleSearch() { + // Clear filteredTokens to show all data. + if (!this.#query) { + this.filteredTokens = null; + } + + let filteredTokens = Object.entries(this.tokensData).reduce( + (acc, [key, tokens]) => { + if (key.includes(this.#query)) { + return { ...acc, [key]: tokens }; + } + let filteredItems = tokens.filter(({ name: tokenName, value }) => + this.filterTokens(this.#query, tokenName, value) + ); + if (filteredItems.length) { + return { ...acc, [key]: filteredItems }; + } + return acc; + }, + {} + ); + this.filteredTokens = filteredTokens; + } + + filterTokens(searchTerm, tokenName, tokenVal) { + if ( + tokenName.includes(searchTerm) || + (typeof tokenVal == "string" && tokenVal.includes(searchTerm)) + ) { + return true; + } + if (typeof tokenVal == "object") { + return Object.entries(tokenVal).some(([key, val]) => + this.filterTokens(searchTerm, key, val) + ); + } + return false; + } + + handleClearSearch(e) { + this.#query = ""; + e.preventDefault(); + this.handleSearch(); + } + + render() { + if (!this.tokensData) { + return ""; + } + + return html` + <link rel="stylesheet" href=${styles} /> + <div class="page-wrapper"> + <div class="filters-wrapper"> + <div class="search-wrapper"> + <div class="search-icon"></div> + <input + type="search" + placeholder="Filter tokens" + @input=${this.debounce(this.handleInput)} + .value=${this.#query} + /> + <div + class="clear-icon" + role="button" + title="Clear" + ?hidden=${!this.#query} + @click=${this.handleClearSearch} + ></div> + </div> + <fieldset id="surface" @change=${this.handleSurfaceChange}> + <label> + <input + type="radio" + name="surface" + value="brand" + ?checked=${this.surface == "brand"} + /> + In-content + </label> + <label> + <input + type="radio" + name="surface" + value="platform" + ?checked=${this.surface == "platform"} + /> + Chrome + </label> + </fieldset> + </div> + <div class="tables-wrapper"> + ${Object.entries(this.filteredTokens ?? this.tokensData).map( + ([tableName, tableEntries]) => { + return html` + <tokens-table + name=${tableName} + surface=${this.surface} + .tokens=${tableEntries} + > + </tokens-table> + `; + } + )} + </div> + </div> + `; + } +} +customElements.define("tables-page", TablesPage); + +/** + * + */ +class TokensTable extends LitElement { + TEMPLATES = { + "font-size": this.fontTemplate, + "font-weight": this.fontTemplate, + "icon-color": this.iconTemplate, + "icon-size": this.iconTemplate, + link: this.linkTemplate, + margin: this.spaceAndSizeTemplate, + "min-height": this.spaceAndSizeTemplate, + outline: this.outlineTemplate, + padding: this.paddingTemplate, + size: this.spaceAndSizeTemplate, + space: this.spaceAndSizeTemplate, + "text-color": this.fontTemplate, + }; + + static properties = { + name: { type: String }, + tokens: { type: Array }, + surface: { type: String }, + }; + + createRenderRoot() { + return this; + } + + getDisplayValues(theme, token) { + let value = this.getResolvedValue(theme, token); + let raw = this.getRawValue(theme, value); + return { value, ...(raw !== value && { raw }) }; + } + + // Return the value with variable references. + // e.g. var(--color-white) + getResolvedValue(theme, token) { + if (typeof token == "string" || typeof token == "number") { + return token; + } + + if (theme == "hcm") { + return ( + token.forcedColors || + token.prefersContrast || + token[this.surface]?.default || + token.default + ); + } + + if (token[this.surface]) { + return this.getResolvedValue(theme, token[this.surface]); + } + + if (token[theme]) { + return token[theme]; + } + + return token.default; + } + + // Return the value with any variables replaced by what they represent. + // e.g. #ffffff + getRawValue(theme, token) { + let cssRegex = /(?<var>var\(--(?<lookupKey>[a-z-0-9,\s]+)\))/; + let matches = cssRegex.exec(token); + if (matches) { + let variableValue = variableLookupTable[matches.groups?.lookupKey]; + if (typeof variableValue == "undefined") { + return token; + } + if (typeof variableValue == "object") { + variableValue = this.getResolvedValue(theme, variableValue); + } + let rawValue = token.replace(matches.groups?.var, variableValue); + if (rawValue.match(cssRegex)) { + return this.getRawValue(theme, rawValue); + } + return rawValue; + } + return token; + } + + getTemplate(value, tokenName, category = this.name) { + // 0 is a valid value + if (value == undefined) { + return "N/A"; + } + + let templateFn = this.TEMPLATES[category]?.bind(this); + if (templateFn) { + return templateFn(category, value, tokenName); + } + + return html` + <div + class="default-preview" + style="${this.getDisplayProperty(category)}: ${HCM_MAP[value] ?? value}" + ></div> + `; + } + + outlineTemplate(_, value, tokenName) { + let property = tokenName.replaceAll(/--|focus-|-error/g, ""); + if (property == "outline-inset") { + property = "outline-offset"; + } + return html` + <div + class="outline-preview" + style="${property}: ${HCM_MAP[value] ?? value};" + ></div> + `; + } + + fontTemplate(category, value) { + return html` + <div class="text-wrapper"> + <p + style="${this.getDisplayProperty(category)}: ${HCM_MAP[value] ?? + value};" + > + The quick brown fox jumps over the lazy dog + </p> + </div> + `; + } + + iconTemplate(_, value, tokenName) { + let property = tokenName.includes("color") ? "background-color" : "height"; + return html` + <div + class="icon-preview" + style="${property}: ${HCM_MAP[value] ?? value};" + ></div> + `; + } + + linkTemplate(_, value, tokenName) { + let hasOutline = tokenName.includes("outline"); + return html` + <a + class=${classMap({ "link-preview": true, outline: hasOutline })} + style="color: ${HCM_MAP[value] ?? value};" + > + Click me! + </a> + `; + } + + spaceAndSizeTemplate(_, value, tokenName) { + let isSize = tokenName.includes("size") || tokenName.includes("height"); + let items = isSize + ? html` + <div class="item" style="height: ${value};width: ${value};"></div> + ` + : html` + <div class="item"></div> + <div class="item"></div> + `; + + return html` + <div + class="space-size-preview space-size-background" + style="gap: ${value};" + > + ${items} + </div> + `; + } + + paddingTemplate(_, value) { + return html` + <div class="space-size-background"> + <div class="padding-item outer" style="padding: ${value}"> + <div class="padding-item inner"></div> + </div> + </div> + `; + } + + getDisplayProperty(category) { + switch (category) { + case "attention-dot": + case "color": + return "background-color"; + case "text-color": + return "color"; + default: + return category.replace("button", ""); + } + } + + cellTemplate(type, tokenValue, tokenName) { + let { value, raw } = this.getDisplayValues("default", tokenValue); + return html` + <td> + <div class="preview-wrapper"> + ${type == "preview" + ? html`${this.getTemplate(raw ?? value, tokenName)}` + : html`<p class="value">${value}</p> + ${raw ? html`<p class="value">${raw}</p>` : ""}`} + </div> + </td> + `; + } + + themeCellTemplate(theme, tokenValue, tokenName) { + let { value, raw } = this.getDisplayValues(theme, tokenValue); + return html` + <td class="${theme}-theme"> + <div class="preview-wrapper"> + ${this.getTemplate(raw ?? value, tokenName)} + <p class="value">${value}</p> + ${raw ? html`<p class="value">${raw}</p>` : ""} + </div> + </td> + `; + } + + render() { + let themedTable = THEMED_TABLES.includes(this.name); + return html` + <details class="table-wrapper" open=""> + <summary class="table-heading"> + <h3>${this.name}</h3> + </summary> + <table> + <thead> + <tr> + <th>Name</th> + ${themedTable + ? html` + <th>Light</th> + <th>Dark</th> + <th>High contrast</th> + ` + : html` + <th>Value</th> + <th>Preview</th> + `} + </tr> + </thead> + <tbody> + ${this.tokens.map(({ name: tokenName, value }) => { + return html`<tr id=${tokenName}> + <td>${tokenName}</td> + ${themedTable + ? html` + ${this.themeCellTemplate("light", value, tokenName)} + ${this.themeCellTemplate("dark", value, tokenName)} + ${this.themeCellTemplate("hcm", value, tokenName)} + ` + : html` + ${this.cellTemplate("value", value, tokenName)} + ${this.cellTemplate("preview", value, tokenName)} + `} + </tr>`; + })} + </tbody> + </table> + </details> + `; + } +} +customElements.define("tokens-table", TokensTable); + +export const Default = () => { + return html`<tables-page></tables-page>`; +}; diff --git a/toolkit/themes/shared/desktop-jar.inc.mn b/toolkit/themes/shared/desktop-jar.inc.mn index 6afb075ea2..226faf26ff 100644 --- a/toolkit/themes/shared/desktop-jar.inc.mn +++ b/toolkit/themes/shared/desktop-jar.inc.mn @@ -32,8 +32,8 @@ skin/classic/global/datetimeinputpickers.css (../../shared/datetimeinputpickers.css) skin/classic/global/design-system/text-and-typography.css(../../shared/design-system/text-and-typography.css) skin/classic/global/design-system/tokens-brand.css (../../shared/design-system/tokens-brand.css) - skin/classic/global/design-system/tokens-shared.css (../../shared/design-system/tokens-shared.css) skin/classic/global/design-system/tokens-platform.css (../../shared/design-system/tokens-platform.css) + skin/classic/global/design-system/tokens-shared.css (../../shared/design-system/tokens-shared.css) skin/classic/global/error-pages.css (../../shared/error-pages.css) skin/classic/global/findbar.css (../../shared/findbar.css) skin/classic/global/global-shared.css (../../shared/global-shared.css) @@ -79,6 +79,7 @@ skin/classic/global/icons/developer.svg (../../shared/icons/developer.svg) skin/classic/global/icons/edit.svg (../../shared/icons/edit.svg) skin/classic/global/icons/edit-copy.svg (../../shared/icons/edit-copy.svg) + skin/classic/global/icons/edit-outline.svg (../../shared/icons/edit-outline.svg) skin/classic/global/icons/error.svg (../../shared/icons/error.svg) skin/classic/global/icons/experiments.svg (../../shared/icons/experiments.svg) skin/classic/global/icons/folder.svg (../../shared/icons/folder.svg) @@ -91,6 +92,7 @@ skin/classic/global/icons/lightbulb.svg (../../shared/icons/lightbulb.svg) skin/classic/global/icons/link.svg (../../shared/icons/link.svg) skin/classic/global/icons/loading.png (../../shared/icons/loading.png) + skin/classic/global/icons/loading.svg (../../shared/icons/loading.svg) skin/classic/global/icons/loading@2x.png (../../shared/icons/loading@2x.png) skin/classic/global/icons/mdn.svg (../../shared/icons/mdn.svg) skin/classic/global/icons/more.svg (../../shared/icons/more.svg) @@ -124,7 +126,6 @@ skin/classic/global/icons/arrow-up.svg (../../shared/icons/arrow-up.svg) skin/classic/global/icons/warning.svg (../../shared/icons/warning.svg) skin/classic/global/icons/warning-fill-12.svg (../../shared/icons/warning-fill-12.svg) - skin/classic/global/icons/whatsnew.svg (../../shared/icons/whatsnew.svg) skin/classic/global/illustrations/about-rights.svg (../../shared/illustrations/about-rights.svg) skin/classic/global/illustrations/about-license.svg (../../shared/illustrations/about-license.svg) skin/classic/global/illustrations/error-malformed-url.svg (../../shared/illustrations/error-malformed-url.svg) @@ -147,7 +148,6 @@ skin/classic/global/reader/RM-Minus-24x24.svg (../../shared/reader/RM-Minus-24x24.svg) skin/classic/global/reader/RM-Plus-24x24.svg (../../shared/reader/RM-Plus-24x24.svg) skin/classic/global/reader/RM-Type-Controls-24x24.svg (../../shared/reader/RM-Type-Controls-24x24.svg) - skin/classic/global/reader/RM-Type-Controls-Arrow.svg (../../shared/reader/RM-Type-Controls-Arrow.svg) skin/classic/global/reader/RM-Content-Width-Minus-42x16.svg (../../shared/reader/RM-Content-Width-Minus-42x16.svg) skin/classic/global/reader/RM-Content-Width-Plus-44x16.svg (../../shared/reader/RM-Content-Width-Plus-44x16.svg) skin/classic/global/reader/RM-Line-Height-Minus-38x14.svg (../../shared/reader/RM-Line-Height-Minus-38x14.svg) diff --git a/toolkit/themes/shared/desktop-non-mac.jar.inc.mn b/toolkit/themes/shared/desktop-non-mac.jar.inc.mn index 072678face..c976798df5 100644 --- a/toolkit/themes/shared/desktop-non-mac.jar.inc.mn +++ b/toolkit/themes/shared/desktop-non-mac.jar.inc.mn @@ -11,7 +11,6 @@ #include desktop-jar.inc.mn skin/classic/global/dialog.css (../../windows/global/dialog.css) - skin/classic/global/tabprompts.css (../../windows/global/tabprompts.css) skin/classic/global/wizard.css (../../windows/global/wizard.css) skin/classic/global/arrow/panelarrow-vertical.svg (../../windows/global/arrow/panelarrow-vertical.svg) diff --git a/toolkit/themes/shared/findbar.css b/toolkit/themes/shared/findbar.css index 714324929e..a3c2ca09b7 100644 --- a/toolkit/themes/shared/findbar.css +++ b/toolkit/themes/shared/findbar.css @@ -2,10 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); +@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); @namespace html url("http://www.w3.org/1999/xhtml"); -findbar { +xul|findbar { border-top: 1px solid ThreeDShadow; min-width: 1px; transition-property: margin-bottom, visibility; @@ -14,29 +14,31 @@ findbar { padding-block: 6px; background-color: -moz-dialog; color: -moz-dialogtext; -} -findbar[hidden] { - /* Override display:none to make the transition work. */ - display: flex; - margin-bottom: calc(-1 * (28px + 12px + 1px)); /* findbar-container's height + padding-block + top border */ - visibility: collapse; - transition-delay: 0s, 150ms; -} + &:where([hidden]) { + /* Override display:none to make the transition work. */ + display: flex; + margin-bottom: calc(-1 * (28px + 12px + 1px)); /* findbar-container's height + padding-block + top border */ + visibility: collapse; + transition-delay: 0s, 150ms; + + > .findbar-container, + > .findbar-closebutton { + opacity: 0; + } + } -@media (prefers-reduced-motion: reduce) { - findbar, - findbar[hidden] { + @media (prefers-reduced-motion) { transition-duration: 0s; transition-delay: 0s; } -} -findbar[noanim], -findbar[noanim] > .findbar-container, -findbar[noanim] > .findbar-closebutton { - transition-duration: 0s; - transition-delay: 0s; + &[noanim], + &[noanim] > .findbar-container, + &[noanim] > .findbar-closebutton { + transition-duration: 0s; + transition-delay: 0s; + } } .findbar-container { @@ -46,10 +48,6 @@ findbar[noanim] > .findbar-closebutton { transition: opacity 150ms ease-in-out; } -findbar[hidden] > .findbar-container { - opacity: 0; -} - /* Remove start margin when close button is on the left side (on macOS) */ .findbar-closebutton + .findbar-container { margin-inline-start: 0; @@ -57,7 +55,7 @@ findbar[hidden] > .findbar-container { /* Search field */ -html|input.findbar-textbox { +.findbar-textbox { appearance: none; background-color: var(--input-bgcolor, var(--toolbar-field-background-color)); color: var(--input-color, var(--toolbar-field-color)); @@ -69,39 +67,39 @@ html|input.findbar-textbox { padding-inline-start: 8px; width: 18em; box-sizing: border-box; -} -html|input.findbar-textbox::placeholder { - opacity: 0.69; -} + &::placeholder { + opacity: 0.69; + } -html|input.findbar-textbox:focus { - background-color: var(--toolbar-field-focus-background-color); - color: var(--toolbar-field-focus-color); - border-color: transparent; - outline: var(--focus-outline); - outline-offset: var(--focus-outline-inset); - outline-color: var(--toolbar-field-focus-border-color); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.23); -} + &:focus { + background-color: var(--toolbar-field-focus-background-color); + color: var(--toolbar-field-focus-color); + border-color: transparent; + outline: var(--focus-outline); + outline-offset: var(--focus-outline-inset); + outline-color: var(--toolbar-field-focus-border-color); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.23); + } -html|input.findbar-textbox:-moz-lwtheme::selection { - background-color: var(--lwt-toolbar-field-highlight, Highlight); - color: var(--lwt-toolbar-field-highlight-text, HighlightText); -} + :root[lwtheme] &::selection { + background-color: var(--lwt-toolbar-field-highlight, Highlight); + color: var(--lwt-toolbar-field-highlight-text, HighlightText); + } -html|input.findbar-textbox:not(:focus):-moz-lwtheme::selection { - background-color: var(--lwt-toolbar-field-highlight, text-select-background-disabled); -} + :root[lwtheme] &:not(:focus)::selection { + background-color: var(--lwt-toolbar-field-highlight, text-select-background-disabled); + } -html|input.findbar-textbox[status="notfound"] { - background-color: rgba(226,40,80,.3); - color: inherit; -} + &[status="notfound"] { + background-color: rgba(226,40,80,.3); + color: inherit; + } -html|input.findbar-textbox[flash="true"] { - background-color: rgba(255,233,0,.3); - color: inherit; + &[flash="true"] { + background-color: rgba(255,233,0,.3); + color: inherit; + } } /* Previous/next buttons */ @@ -119,16 +117,18 @@ html|input.findbar-textbox[flash="true"] { outline: var(--focus-outline); outline-offset: var(--focus-outline-inset); } -} -.findbar-find-previous:not([disabled]):hover, -.findbar-find-next:not([disabled]):hover { - background-color: var(--toolbarbutton-hover-background, rgba(190,190,190,.2)); -} + &:not([disabled]):hover { + background-color: var(--toolbarbutton-hover-background, rgba(190,190,190,.2)); + } + + &:not([disabled]):hover:active { + background-color: var(--toolbarbutton-active-background, rgba(190,190,190,.4)); + } -.findbar-find-previous:not([disabled]):hover:active, -.findbar-find-next:not([disabled]):hover:active { - background-color: var(--toolbarbutton-active-background, rgba(190,190,190,.4)); + &[disabled="true"] > .toolbarbutton-icon { + opacity: var(--toolbarbutton-disabled-opacity); + } } .findbar-find-previous { @@ -141,11 +141,6 @@ html|input.findbar-textbox[flash="true"] { margin-inline: 0 8px; } -.findbar-find-previous[disabled="true"] > .toolbarbutton-icon, -.findbar-find-next[disabled="true"] > .toolbarbutton-icon { - opacity: var(--toolbarbutton-disabled-opacity); -} - /* Checkboxes & Labels */ .findbar-container > checkbox, @@ -175,17 +170,13 @@ html|input.findbar-textbox[flash="true"] { width: 24px; fill: var(--toolbarbutton-icon-fill); transition: opacity 150ms ease-in-out; -} - -findbar[hidden] > .findbar-closebutton { - opacity: 0; -} -.close-icon.findbar-closebutton:hover { - background-color: var(--toolbarbutton-hover-background, rgba(190,190,190,.2)); - outline: none; -} + &:hover { + background-color: var(--toolbarbutton-hover-background, rgba(190,190,190,.2)); + outline: none; + } -.close-icon.findbar-closebutton:hover:active { - background-color: var(--toolbarbutton-active-background, rgba(190,190,190,.4)); + &:hover:active { + background-color: var(--toolbarbutton-active-background, rgba(190,190,190,.4)); + } } diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/global-shared.css index 320245afbe..f5b5aa9fbb 100644 --- a/toolkit/themes/shared/global-shared.css +++ b/toolkit/themes/shared/global-shared.css @@ -48,7 +48,7 @@ --toolbar-bgcolor: var(--toolbar-non-lwt-bgcolor); --toolbar-color: var(--toolbar-non-lwt-textcolor); - &:-moz-lwtheme { + &[lwtheme] { --toolbar-bgcolor: rgba(255,255,255,.4); --toolbar-color: var(--lwt-text-color, inherit); } @@ -72,7 +72,7 @@ --panel-separator-color: currentColor; --toolbar-field-focus-border-color: var(--focus-outline-color); - &:not(:-moz-lwtheme) { + &:not([lwtheme]) { --panel-disabled-color: GrayText; } } @@ -96,7 +96,7 @@ /* Lightweight theme roots */ -:root:-moz-lwtheme { +:root[lwtheme] { toolbar, &[lwt-popup="light"] panel { color-scheme: light; @@ -151,7 +151,7 @@ html|textarea { color: var(--input-color, FieldText); &:where(:user-invalid) { - border-color: var(--color-error-outline); + border-color: var(--outline-color-error); } &:where(:focus-visible) { diff --git a/toolkit/themes/shared/icons/edit-outline.svg b/toolkit/themes/shared/icons/edit-outline.svg new file mode 100644 index 0000000000..aef1625941 --- /dev/null +++ b/toolkit/themes/shared/icons/edit-outline.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"> +<path d="M0.0184994 13.6645L0.612501 10.4635C0.687501 10.0545 0.884501 9.6805 1.1805 9.3825L9.9815 0.5805C10.7555 -0.1925 12.0145 -0.1945 12.7915 0.5805L14.4195 2.2085C15.1935 2.9835 15.1935 4.2435 14.4195 5.0185L5.6155 13.8215C5.3195 14.1165 4.9455 14.3125 4.5375 14.3875L1.3355 14.9815C1.2655 14.9935 1.1975 15.0005 1.1295 15.0005C0.8325 15.0005 0.544499 14.8835 0.3305 14.6695C0.0674992 14.4055 -0.0495005 14.0305 0.0184994 13.6645ZM12.4715 5.1965L13.6315 4.0365L13.6305 3.1885L11.8105 1.3675L10.9625 1.3685L9.8025 2.5285L12.4715 5.1965ZM4.3105 13.1585C4.4705 13.1285 4.6175 13.0515 4.7335 12.9345L11.5865 6.0815L8.9185 3.4135L2.0655 10.2655C1.9485 10.3835 1.8715 10.5305 1.8405 10.6915L1.3665 13.2485L1.7515 13.6335L4.3105 13.1585Z"/> +</svg> diff --git a/toolkit/themes/shared/icons/loading.svg b/toolkit/themes/shared/icons/loading.svg new file mode 100644 index 0000000000..1a10324c5a --- /dev/null +++ b/toolkit/themes/shared/icons/loading.svg @@ -0,0 +1,16 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg id="loading-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="12" height="12" fill="context-fill"> + <style> + @keyframes loadingSVGRotate { + from { rotate: 0; } to { rotate: 360deg } + } + #loading-svg { + animation: loadingSVGRotate 1.2s linear infinite; + transform-origin: 50% 50%; + } + </style> + <path d="M8.9 3.8c-.2-.2-.1-.5.1-.7.2-.1.6-.1.7.2.5.7.8 1.6.8 2.5 0 2.5-2 4.5-4.5 4.5l0 1.5c0 .2-.2.3-.3.1l-2-1.9 0-.4 1.9-1.9c.2-.2.4-.1.4.1l0 1.5c1.9 0 3.5-1.6 3.5-3.5 0-.7-.2-1.4-.6-2z"/> + <path d="M3.1 8.2c.2.2.1.5-.1.7-.2.1-.6.1-.7-.2-.5-.7-.8-1.6-.8-2.5 0-2.5 2-4.5 4.5-4.5L6 .2c0-.2.2-.3.3-.1l2 1.9 0 .4-2 2c-.1.1-.3 0-.3-.2l0-1.5c-1.9 0-3.5 1.6-3.5 3.5 0 .7.2 1.4.6 2z"/> +</svg> diff --git a/toolkit/themes/shared/icons/whatsnew.svg b/toolkit/themes/shared/icons/whatsnew.svg deleted file mode 100644 index b77d0165a6..0000000000 --- a/toolkit/themes/shared/icons/whatsnew.svg +++ /dev/null @@ -1,10 +0,0 @@ -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> - <path fill="context-fill" fill-opacity="context-fill-opacity" d="M2 13.76A1.23 1.23 0 0 0 3.24 15H7V9H2z"/> - <path fill="context-fill" fill-opacity="context-fill-opacity" d="M9 9v6h3.76A1.23 1.23 0 0 0 14 13.76V9H9z"/> - <path fill="context-fill" fill-opacity="context-fill-opacity" d="M1 5v3h6V4H2a1 1 0 0 0-1 1z"/> - <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14 4H9v4h6V5a1 1 0 0 0-1-1z"/> - <path fill="context-fill" fill-opacity="context-fill-opacity" d="M10.05.41A1.34 1.34 0 0 0 8 1a1.35 1.35 0 0 0-2-.59C5.2 1.08 4.91 2.48 8 4c3.09-1.52 2.8-2.92 2.05-3.59z"/> -</svg> diff --git a/toolkit/themes/shared/in-content/common-shared.css b/toolkit/themes/shared/in-content/common-shared.css index c8f4f3efd2..86c6d36210 100644 --- a/toolkit/themes/shared/in-content/common-shared.css +++ b/toolkit/themes/shared/in-content/common-shared.css @@ -14,9 +14,9 @@ :host(:is(.anonymous-content-host, notification-message)), :root { --in-content-page-color: rgb(21, 20, 26); - --in-content-page-background: #fff; + --in-content-page-background: var(--background-color-canvas); --in-content-text-color: var(--in-content-page-color); - --in-content-box-background: var(--box-background-color); + --in-content-box-background: var(--background-color-box); --in-content-box-background-odd: rgba(12, 12, 13, 0.05); /* grey 90 a05 */ --in-content-box-border-color: color-mix(in srgb, currentColor 41%, transparent); --in-content-box-info-background: #f0f0f4; @@ -122,7 +122,6 @@ * just replace most of these for system colors and remove all this * duplication (assuming we honor the preferred color scheme for * in-content privileged pages and plain-text documents). */ - --in-content-page-background: rgb(28,27,34); --in-content-page-color: rgb(251,251,254); --in-content-box-background-odd: rgba(249,249,250,0.05); @@ -156,20 +155,19 @@ } /* For dialogs, use a different background colour. We don't do - * this in High Contrast mode, as we should be using system colours then. + * this in forced colors mode, as we should be using system colours then. */ - @media not (prefers-contrast) { + @media not (forced-colors) { :root[dialogroot] { --in-content-page-background: #42414d; } } } -@media (prefers-contrast) { +@media (forced-colors) { :host(:is(.anonymous-content-host, notification-message)), :root { --in-content-page-color: CanvasText; - --in-content-page-background: Canvas; --in-content-box-background-odd: var(--in-content-box-background); --in-content-box-border-color: -moz-DialogText; @@ -304,7 +302,7 @@ xul|tab[selected]:hover:active { background-color: var(--in-content-button-background-active); } -@media (prefers-contrast) { +@media (forced-colors) { xul|tab:hover, xul|tab:hover:active { border-bottom-color: currentColor; @@ -483,7 +481,7 @@ xul|button.primary:not([disabled="true"]):hover:active { border-color: var(--in-content-primary-button-border-active); } -@media not (prefers-contrast) { +@media not (forced-colors) { html|button.semi-transparent:not(.ghost-button, .primary):enabled { background-color: color-mix(in srgb, currentColor 10%, transparent); } @@ -504,7 +502,7 @@ xul|button.primary:not([disabled="true"]):hover:active { --in-content-focus-outline-color: var(--in-content-danger-button-background); } -@media not (prefers-contrast) { +@media not (forced-colors) { html|button.ghost-button { background-color: transparent; } @@ -798,7 +796,7 @@ html|*#categories > html|*.category:dir(rtl) { border-color: var(--in-content-button-border-color-active); } -@media not (prefers-contrast) { +@media not (forced-colors) { #categories > .category[selected], #categories > .category.selected { color: var(--in-content-accent-color); @@ -815,7 +813,7 @@ html|*#categories > html|*.category:dir(rtl) { } } -@media (prefers-contrast) { +@media (forced-colors) { #categories > .category { /* The transition causes issues with the text getting a background while * transitioning and it looks weird. */ @@ -963,7 +961,7 @@ xul|treechildren::-moz-tree-row(selected) { color: var(--in-content-item-selected-text); } -@media not (prefers-contrast) { +@media not (forced-colors) { xul|richlistbox:not(#categories) > xul|richlistitem[selected] { /* Ensure buttons/menulists inside richlistitems (containers, applications) look OK */ --in-content-button-background: color-mix(in srgb, currentColor 15%, transparent); @@ -981,8 +979,8 @@ xul|richlistitem[selected] xul|menulist:focus-visible { } /* Use a 2px border so that selected row highlight is still visible behind - an existing high-contrast border that uses the background color */ -@media (prefers-contrast) { + an existing forced colors border that uses the background color */ +@media (forced-colors) { xul|treechildren::-moz-tree-row(selected) { border: 2px solid currentColor; border-radius: 4px; @@ -1046,7 +1044,7 @@ xul|treecol:not([hideheader="true"], :first-child), border-image: linear-gradient(transparent 0%, transparent 20%, var(--in-content-box-border-color) 20%, var(--in-content-box-border-color) 80%, transparent 80%, transparent 100%) 1 1; } -@media (prefers-contrast) { +@media (forced-colors) { xul|treecol:not([hideheader="true"], :first-child), xul|treecolpicker { --in-content-box-border-color: var(--in-content-button-border-color); @@ -1191,7 +1189,7 @@ input[type="text"][warning]:enabled:not(:focus) { align-items: center; } -@media (prefers-contrast) { +@media (forced-colors) { .sidebar-footer-link { /* We need a true transparent but in HCM this would compute to an actual color, * so select the page's background color instead: */ diff --git a/toolkit/themes/shared/narrate.css b/toolkit/themes/shared/narrate.css index fbda43841e..9ba7015215 100644 --- a/toolkit/themes/shared/narrate.css +++ b/toolkit/themes/shared/narrate.css @@ -16,12 +16,17 @@ body.sepia { --narrating-paragraph-background-color: #e0d7c5; } -body.dark { +body.dark, +body.contrast { --current-voice: #a09eac; --narrate-word-highlight-color: #6f6f6f; --narrating-paragraph-background-color: #242424; } +body.custom { + --narrating-paragraph-background-color: var(--custom-theme-selection-highlight); +} + body.hcm { --current-voice: inherit; --narrate-word-highlight-color: SelectedItem; @@ -100,14 +105,6 @@ body.hcm .speaking:not(.open) .narrate-toggle:hover { outline-offset: -2px; } -.narrate-dropdown > .dropdown-popup { - top: -34px; -} - -.narrate-dropdown .dropdown-arrow { - top: 40px; -} - .narrate-row { display: flex; align-items: center; @@ -168,7 +165,7 @@ body.hcm .speaking:not(.open) .narrate-toggle:hover { background-repeat: no-repeat; background-size: 24px auto; -moz-context-properties: fill; - fill: var(--icon-fill); + fill: var(--popup-button-foreground); } .narrate-rate::before { @@ -193,7 +190,7 @@ body.hcm .speaking:not(.open) .narrate-toggle:hover { } .narrate-rate-input::-moz-range-track { - background-color: var(--icon-fill); + background-color: var(--popup-button-foreground); height: 2px; } @@ -203,7 +200,7 @@ body.hcm .speaking:not(.open) .narrate-toggle:hover { } .narrate-rate-input::-moz-range-thumb { - background-color: var(--icon-fill); + background-color: var(--popup-button-foreground); height: 16px; width: 16px; border-radius: 8px; @@ -244,6 +241,10 @@ body.hcm .speaking:not(.open) .narrate-toggle:hover { color: var(--current-voice); } +.voiceselect .label { + color: var(--popup-button-foreground); +} + .voiceselect > .options { display: none; overflow-y: auto; @@ -256,6 +257,7 @@ body.hcm .speaking:not(.open) .narrate-toggle:hover { .voiceselect > .options > button.option { box-sizing: border-box; + color: var(--popup-button-foreground); } .voiceselect > .options > button.option:not(:first-child) { diff --git a/toolkit/themes/shared/notification.css b/toolkit/themes/shared/notification.css index b94918cce8..8ddab7fff2 100644 --- a/toolkit/themes/shared/notification.css +++ b/toolkit/themes/shared/notification.css @@ -41,15 +41,15 @@ notification { } @media (prefers-color-scheme: dark) { - notification[type="info"]:-moz-lwtheme { - --notification-background: #38383d; - --notification-text: rgb(249, 249, 250); - } - notification[type="info"] { --notification-button-background: rgba(249,249,250,.1); --notification-button-background-hover: rgba(249,249,250,.2); --notification-button-background-active: rgba(249,249,250,.3); + + *|*:root[lwtheme] & { + --notification-background: #38383d; + --notification-text: rgb(249, 249, 250); + } } } diff --git a/toolkit/themes/shared/pictureinpicture/player.css b/toolkit/themes/shared/pictureinpicture/player.css index f29d68e429..0395408191 100644 --- a/toolkit/themes/shared/pictureinpicture/player.css +++ b/toolkit/themes/shared/pictureinpicture/player.css @@ -326,31 +326,29 @@ body:fullscreen #controls[showing]:hover { background-color: transparent; padding: 6px 2px; margin: 0; -} -#audio-scrubber::-moz-range-thumb { - border-radius: 8px; - background-color: #FFFFFF; - position: relative; - width: 8px; - height: 8px; - bottom: 24px; - padding: 0; -} + &::-moz-range-thumb { + border-radius: 8px; + background-color: #FFFFFF; + width: 8px; + height: 8px; + padding: 0; + } -#audio-scrubber::-moz-range-track { - background-color: #969696; -} + &::-moz-range-track { + background-color: #969696; + } -#audio-scrubber::-moz-range-progress { - background-color: #FFFFFF; -} + &::-moz-range-progress { + background-color: #FFFFFF; + } -#audio-scrubber, -#audio-scrubber::-moz-range-track, -#audio-scrubber::-moz-range-progress { - height: 2px; - border-radius: 10px; + &, + &::-moz-range-track, + &::-moz-range-progress { + height: 2px; + border-radius: 10px; + } } #fullscreen { @@ -594,32 +592,30 @@ input:checked + .slider::before { width: 100%; background-color: transparent; padding: 6px 2px; -} -#scrubber::-moz-range-thumb { - border-radius: 14px; - background-color: #BFBFC9; - position: relative; - width: 8px; - height: 8px; - border: 3px solid #FFFFFF; - bottom: 24px; - padding: 0; -} + &::-moz-range-thumb { + border-radius: 14px; + background-color: #BFBFC9; + width: 8px; + height: 8px; + border: 3px solid #FFFFFF; + padding: 0; + } -#scrubber::-moz-range-track { - background-color: #969696; -} + &::-moz-range-track { + background-color: #969696; + } -#scrubber::-moz-range-progress { - background-color: #FFFFFF; -} + &::-moz-range-progress { + background-color: #FFFFFF; + } -#scrubber, -#scrubber::-moz-range-track, -#scrubber::-moz-range-progress { - height: 4px; - border-radius: 10px; + &, + &::-moz-range-track, + &::-moz-range-progress { + height: 4px; + border-radius: 10px; + } } #timestamp { diff --git a/toolkit/themes/shared/popup.css b/toolkit/themes/shared/popup.css index 1426f9c4f6..ed23086021 100644 --- a/toolkit/themes/shared/popup.css +++ b/toolkit/themes/shared/popup.css @@ -27,7 +27,7 @@ panel { margin: calc(-1 * var(--panel-shadow-margin)); /* Panel design token theming */ - --color-canvas: var(--panel-background); + --background-color-canvas: var(--panel-background); @media (-moz-platform: linux) { --panel-border-radius: 8px; diff --git a/toolkit/themes/shared/reader/RM-Type-Controls-Arrow.svg b/toolkit/themes/shared/reader/RM-Type-Controls-Arrow.svg deleted file mode 100644 index 235f186376..0000000000 --- a/toolkit/themes/shared/reader/RM-Type-Controls-Arrow.svg +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 24"> - <polygon points="16.58 0.01 16.57 0 4.58 12 16.57 24 16.58 23.98" fill="context-fill" stroke="context-stroke"/> -</svg> diff --git a/toolkit/themes/windows/global/menu.css b/toolkit/themes/windows/global/menu.css index 4868f41ed1..4889de0003 100644 --- a/toolkit/themes/windows/global/menu.css +++ b/toolkit/themes/windows/global/menu.css @@ -51,20 +51,22 @@ menubar > menu[_moz-menuactive="true"]:not([disabled="true"]) { color: -moz-menubarhovertext; } -menubar > menu:-moz-lwtheme { - appearance: none; - border-color: transparent; +*|*:root[lwtheme] { + menubar > menu { + appearance: none; + border-color: transparent; + + &:not([disabled="true"]) { + color: inherit; + } + &[_moz-menuactive="true"]:not([disabled="true"]) { + background-color: SelectedItem; + color: SelectedItemText; + text-shadow: none; + } + } } -menubar > menu:-moz-lwtheme:not([disabled="true"]) { - color: inherit; -} - -menubar > menu:-moz-lwtheme[_moz-menuactive="true"]:not([disabled="true"]) { - background-color: SelectedItem; - color: SelectedItemText; - text-shadow: none; -} /* ..... internal content .... */ diff --git a/toolkit/themes/windows/global/tabprompts.css b/toolkit/themes/windows/global/tabprompts.css deleted file mode 100644 index 2b5fbdf55f..0000000000 --- a/toolkit/themes/windows/global/tabprompts.css +++ /dev/null @@ -1,25 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Tab Modal Prompt boxes */ -.tabModalBackground, -tabmodalprompt { - background-color: hsla(0,0%,10%,.5); -} - -tabmodalprompt { - font-family: sans-serif; /* use content font not system UI font */ -} - -.tabmodalprompt-mainContainer { - color: FieldText; - background-color: Field; - border-radius: 2px; - border: 1px solid threeDDarkShadow; -} - -.tabmodalprompt-buttonContainer { - background-color: hsla(0,0%,0%,.05); - border-top: 1px solid hsla(0,0%,0%,.05); -} diff --git a/toolkit/themes/windows/global/toolbar.css b/toolkit/themes/windows/global/toolbar.css index 45b79eafdd..ada6f77f9c 100644 --- a/toolkit/themes/windows/global/toolbar.css +++ b/toolkit/themes/windows/global/toolbar.css @@ -8,14 +8,7 @@ @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); -toolbox { - appearance: auto; - -moz-default-appearance: toolbox; -} - toolbar { - appearance: auto; - -moz-default-appearance: toolbar; min-width: 1px; min-height: 19px; } @@ -24,11 +17,6 @@ toolbar:first-child { min-width: 1px; } -toolbox:-moz-lwtheme, -toolbar:-moz-lwtheme { - appearance: none; -} - toolbarseparator { appearance: auto; -moz-default-appearance: separator; |