diff --git a/.changeset/rude-lies-smile.md b/.changeset/rude-lies-smile.md new file mode 100644 index 0000000000..f364df65bf --- /dev/null +++ b/.changeset/rude-lies-smile.md @@ -0,0 +1,21 @@ +--- +"@digdir/designsystemet-react": major +"@digdir/designsystemet-css": minor +"@digdir/designsystemet-theme": minor +"@digdir/designsystemet": minor +--- + +React components and css now support custom colors through the `data-color` attribute. + +**BREAKING CHANGE**: All React components that had a `color` prop have been changed to use `data-color`. + +All1 css targeting `data-color` has been changed to work with all custom colors generated by the CLI. + +`Avatar`, `Badge`, `Button`, and `Link` use `--ds-color-accent-*`2, unless `data-color` is set directly on the element. + +For components that had a `color` prop, but defaulted to something other than `"accent"`, `data-color` must also be set directly on the element. + +All other components that defaulted to `"accent"`, or previously only existed in `"accent"` color, now support `data-color`. They will also inherit their color from the closest `data-color` attribute. If none is found, they use `--ds-color-accent-*`2. + +1: ...except `Alert`, which only supports `info`, `warning`, `danger` and `success` colors. +2: If an `"accent"` color is not defined in the theme, the `--ds-color-accent-*` variables will point to the first `main-color`. diff --git a/apps/storybook/docs-components/CssVariables/CssVariables.tsx b/apps/storybook/docs-components/CssVariables/CssVariables.tsx index ccf33c2074..2b4eb31e1b 100644 --- a/apps/storybook/docs-components/CssVariables/CssVariables.tsx +++ b/apps/storybook/docs-components/CssVariables/CssVariables.tsx @@ -49,21 +49,32 @@ export const CssVariables = forwardRef( function getCssVariables(css: string) { const res: { [key: string]: string } = {}; - /* get first block of css */ - const cssBlock = css.match(/(?<={)([^}]*)/)?.[0]; - if (!cssBlock) { - return res; - } - - /* Create a temporary element */ - const tempElement = document.createElement('div'); - tempElement.style.cssText = cssBlock; + // temporarily remove inline strings, as they may contain ; and } characters + // and thus ruin the matching for property declarations + const stringsRemovedFromCss = Array.from(css.matchAll(/"[^"]*"/g)).map( + (x) => x[0], + ); + const cssWithRemovedStrings = stringsRemovedFromCss.reduce( + (prev, curr, idx) => prev.replace(curr, ``), + css, + ); + // get all --dsc-* property declarations + const cssVars = Array.from( + cssWithRemovedStrings.matchAll(/(? matches[1]); /* Iterate over the CSS properties */ - for (let i = 0; i < tempElement.style.length; i++) { - const name = tempElement.style[i]; - if (name.startsWith('--dsc')) { - res[name] = tempElement.style.getPropertyValue(name).trim(); + for (const declaration of cssVars) { + const [name, value] = declaration.split(':'); + // Choose the earliest declaration of the property. + // We assume later declarations are part of a sub-selector. + if (!res[name]) { + // Return the original inline string from the value, if it was removed earlier + const valueWithOriginalString = value.replace( + //, + (_, p1: string) => stringsRemovedFromCss[parseInt(p1)], + ); + res[name] = valueWithOriginalString; } } diff --git a/package.json b/package.json index ca8836103a..32ede37394 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ ], "scripts": { "test": "vitest", - "test:cli": "yarn workspace @digdir/designsystemet test", + "test:cli": "yarn workspace @digdir/designsystemet test --verbose", "test:storybook": "yarn workspace @designsystemet/storybook run-and-test-storybook", "test:coverage": "vitest run --coverage", "storybook": "yarn workspace @designsystemet/storybook dev", diff --git a/packages/cli/package.json b/packages/cli/package.json index d6442917f0..9e8a30afdf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,8 +36,8 @@ "build": "tsup && yarn build:types", "build:swc": "yarn clean && swc src bin --copy-files -d dist && yarn build:types", "build:types": "tsc --emitDeclarationOnly --declaration", - "test:tokens-create": "yarn designsystemet tokens create -m dominant:#007682 complimentary:#ff0000 -n #003333 -s support1:#12404f support2:#0054a6 support3:#942977 -w ./test-tokens-create", - "test:tokens-build": "yarn designsystemet tokens build --verbose -t ./test-tokens-create -o ./test-tokens-build", + "test:tokens-create": "yarn designsystemet tokens create -m dominant:#007682 secondary:#ff0000 -n #003333 -s support1:#12404f support2:#0054a6 support3:#942977 -w ./test-tokens-create", + "test:tokens-build": "yarn designsystemet tokens build -t ./test-tokens-create -o ./test-tokens-build", "test:tokens-create-and-build": "rimraf test-tokens-create && rimraf test-tokens-build && yarn test:tokens-create && yarn test:tokens-build", "test": "yarn test:tokens-create-and-build", "clean": "rimraf dist", diff --git a/packages/cli/src/tokens/build.ts b/packages/cli/src/tokens/build.ts index 6db6cced50..d33a6d42d6 100644 --- a/packages/cli/src/tokens/build.ts +++ b/packages/cli/src/tokens/build.ts @@ -11,6 +11,8 @@ import type { BuildConfig, ThemePermutation } from './build/types.js'; import { makeEntryFile } from './build/utils/entryfile.js'; import { processThemeObject } from './build/utils/getMultidimensionalThemes.js'; +export const DEFAULT_COLOR = 'accent'; + type Options = { /** Design tokens path */ tokens: string; @@ -81,11 +83,15 @@ export async function buildTokens(options: Options): Promise { .filter((theme) => R.not(theme.group === 'size' && theme.name !== 'default')); if (!buildOptions.accentColor) { - const firstMainColor = relevant$themes.find((theme) => theme.group === 'main-color'); - buildOptions.accentColor = firstMainColor?.name; + const accentOrFirstMainColor = + relevant$themes.find((theme) => theme.name === DEFAULT_COLOR) || + relevant$themes.find((theme) => theme.group === 'main-color'); + buildOptions.accentColor = accentOrFirstMainColor?.name; } - console.log('default accent color:', buildOptions.accentColor); + if (buildOptions.accentColor !== DEFAULT_COLOR) { + console.log('accent color:', buildOptions.accentColor); + } const buildAndSdConfigs = R.map( (val: BuildConfig) => ({ diff --git a/packages/cli/src/tokens/build/configs.ts b/packages/cli/src/tokens/build/configs.ts index d0246175c6..da4c97f139 100644 --- a/packages/cli/src/tokens/build/configs.ts +++ b/packages/cli/src/tokens/build/configs.ts @@ -4,7 +4,7 @@ import StyleDictionary from 'style-dictionary'; import type { Config as StyleDictionaryConfig, TransformedToken } from 'style-dictionary/types'; import { outputReferencesFilter } from 'style-dictionary/utils'; -import { buildOptions } from '../build.js'; +import { DEFAULT_COLOR, buildOptions } from '../build.js'; import { formats } from './formats/css.js'; import { jsTokens } from './formats/js-tokens.js'; import { nameKebab, resolveMath, sizeRem, typographyName } from './transformers.js'; @@ -70,7 +70,7 @@ export type GetStyleDictionaryConfig = ( options: { outPath?: string; }, -) => StyleDictionaryConfig; +) => StyleDictionaryConfig | { config: StyleDictionaryConfig; permutationOverrides?: Partial }[]; const colorModeVariables: GetStyleDictionaryConfig = ({ mode = 'light', theme }, { outPath }) => { const selector = `${mode === 'light' ? ':root, ' : ''}[data-ds-color-mode="${mode}"]`; @@ -114,7 +114,7 @@ const colorCategoryVariables = const isDefault = color === buildOptions?.accentColor; const selector = `${isDefault ? ':root, ' : ''}[data-color="${color}"]`; - return { + const config: StyleDictionaryConfig = { usesDtcg, preprocessors: ['tokens-studio'], platforms: { @@ -143,6 +143,31 @@ const colorCategoryVariables = }, }, }; + if (isDefault && color !== DEFAULT_COLOR) { + console.log( + `Creating "${DEFAULT_COLOR}" color variables pointing to "${color}", since a color named "${DEFAULT_COLOR}" is not defined`, + ); + // Create a --ds-color-accent-* scale which points to the default color + const defaultColorConfig = R.mergeDeepRight(config, { + platforms: { + css: { + selector: ':root', + files: [ + { + ...config.platforms?.css?.files?.[0], + destination: `color/${DEFAULT_COLOR}.css`, + }, + ], + options: { replaceCategoryWith: DEFAULT_COLOR }, + }, + }, + } satisfies StyleDictionaryConfig); + return [ + { config }, + { config: defaultColorConfig, permutationOverrides: { 'main-color': `${DEFAULT_COLOR} → ${color}` } }, + ]; + } + return config; }; const semanticVariables: GetStyleDictionaryConfig = ({ theme }, { outPath }) => { @@ -308,24 +333,29 @@ export const getConfigsForThemeDimensions = ( const permutations = getMultidimensionalThemes(themes, dimensions); return permutations - .map(({ selectedTokenSets, permutation }) => { + .flatMap(({ selectedTokenSets, permutation }) => { const setsWithPaths = selectedTokenSets.map((x) => `${tokensDir}/${x}.json`); const [source, include] = paritionPrimitives(setsWithPaths); - const config_ = getConfig(permutation, { outPath }); - - const config: StyleDictionaryConfig = { - ...config_, - log: { - ...config_?.log, - verbosity: buildOptions?.verbose ? 'verbose' : 'silent', - }, - source, - include, - }; + const configOrConfigs = getConfig(permutation, { outPath }); + const configs_ = Array.isArray(configOrConfigs) ? configOrConfigs : [{ config: configOrConfigs }]; - return { permutation, config }; + const configs: SDConfigForThemePermutation[] = configs_.map(({ config, permutationOverrides }) => { + return { + permutation: { ...permutation, ...permutationOverrides }, + config: { + ...config, + log: { + ...config?.log, + verbosity: buildOptions?.verbose ? 'verbose' : 'silent', + }, + source, + include, + }, + }; + }); + return configs; }) .sort(); }; diff --git a/packages/cli/src/tokens/build/formats/css.ts b/packages/cli/src/tokens/build/formats/css.ts index df9d479413..edcc3a4dc1 100644 --- a/packages/cli/src/tokens/build/formats/css.ts +++ b/packages/cli/src/tokens/build/formats/css.ts @@ -55,10 +55,16 @@ const colormode: Format = { }, }; +declare module 'style-dictionary/types' { + export interface LocalOptions { + replaceCategoryWith?: string; + } +} + const colorcategory: Format = { name: 'ds/css-colorcategory', format: async ({ dictionary, file, options, platform }) => { - const { outputReferences, usesDtcg } = options; + const { outputReferences, usesDtcg, replaceCategoryWith = '' } = options; const { selector, layer } = platform; const header = await fileHeader({ file }); @@ -72,7 +78,10 @@ const colorcategory: Format = { }), (token: TransformedToken) => ({ ...token, - name: token.name.replace(new RegExp(`-(${colorCategories.main}|${colorCategories.support})-`), '-'), + name: token.name.replace( + new RegExp(`-(${colorCategories.main}|${colorCategories.support})-`), + replaceCategoryWith ? `-${replaceCategoryWith}-` : '-', + ), }), ); diff --git a/packages/cli/src/tokens/build/utils/getMultidimensionalThemes.ts b/packages/cli/src/tokens/build/utils/getMultidimensionalThemes.ts index a9ea14e071..13fa3c5aaf 100644 --- a/packages/cli/src/tokens/build/utils/getMultidimensionalThemes.ts +++ b/packages/cli/src/tokens/build/utils/getMultidimensionalThemes.ts @@ -3,7 +3,7 @@ import { TokenSetStatus } from '@tokens-studio/types'; import chalk from 'chalk'; import { kebabCase } from 'change-case'; import * as R from 'ramda'; -import { buildOptions } from '../../build'; +import { buildOptions } from '../../build.js'; import type { ThemeDimension, ThemePermutation } from '../types'; /** diff --git a/packages/cli/src/tokens/template.ts b/packages/cli/src/tokens/template.ts index e954c99464..7ac74d21b7 100644 --- a/packages/cli/src/tokens/template.ts +++ b/packages/cli/src/tokens/template.ts @@ -2,9 +2,11 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import type { ThemeObject } from '@tokens-studio/types'; import * as R from 'ramda'; -import originalColorJson from '../../../../design-tokens/semantic/color.json'; -import originalColorCategoryJson from '../../../../design-tokens/semantic/modes/main-color/accent.json'; -import originalThemeJson from '../../../../design-tokens/themes/theme.json'; +import originalColorJson from '../../../../design-tokens/semantic/color.json' with { type: 'json' }; +import originalColorCategoryJson from '../../../../design-tokens/semantic/modes/main-color/accent.json' with { + type: 'json', +}; +import originalThemeJson from '../../../../design-tokens/themes/theme.json' with { type: 'json' }; import { stringify } from './write'; const DIRNAME: string = import.meta.dirname || __dirname; diff --git a/packages/cli/src/tokens/write.ts b/packages/cli/src/tokens/write.ts index cb1305ab5f..f84864fe9c 100644 --- a/packages/cli/src/tokens/write.ts +++ b/packages/cli/src/tokens/write.ts @@ -4,11 +4,15 @@ import type { ThemeObject } from '@tokens-studio/types'; import chalk from 'chalk'; import * as R from 'ramda'; import type { ColorMode } from '../colors/types.js'; -import semanticColorBaseFile from './design-tokens/template/semantic/color-base-file.json'; -import customColorTemplate from './design-tokens/template/semantic/modes/category-color/category-color-template.json'; -import semanticColorTemplate from './design-tokens/template/semantic/semantic-color-template.json'; -import themeBaseFile from './design-tokens/template/themes/theme-base-file.json'; -import themeColorTemplate from './design-tokens/template/themes/theme-color-template.json'; +import semanticColorBaseFile from './design-tokens/template/semantic/color-base-file.json' with { type: 'json' }; +import customColorTemplate from './design-tokens/template/semantic/modes/category-color/category-color-template.json' with { + type: 'json', +}; +import semanticColorTemplate from './design-tokens/template/semantic/semantic-color-template.json' with { + type: 'json', +}; +import themeBaseFile from './design-tokens/template/themes/theme-base-file.json' with { type: 'json' }; +import themeColorTemplate from './design-tokens/template/themes/theme-color-template.json' with { type: 'json' }; import type { Collection, Colors, File, Tokens, TokensSet, TypographyModes } from './types.js'; import { generateMetadataJson } from './write/generate$metadata.js'; import { generateThemesJson } from './write/generate$themes.js'; diff --git a/packages/css/accordion.css b/packages/css/accordion.css index 0ec4aaf3f5..b7f7ac4849 100644 --- a/packages/css/accordion.css +++ b/packages/css/accordion.css @@ -1,13 +1,39 @@ .ds-accordion-group { + /* default color: neutral */ --dsc-accordion-background: var(--ds-color-neutral-background-default); + --dsc-accordion-heading-background--hover: var(--ds-color-neutral-surface-default); + --dsc-accordion-heading-background--open: var(--ds-color-neutral-background-subtle); + --dsc-accordion-heading-background: var(--ds-color-neutral-background-default); + --dsc-accordion-border-color: var(--ds-color-neutral-border-subtle); + + &[data-color]:where(:not([data-color='subtle'])) { + --dsc-accordion-background: var(--ds-color-background-subtle); + --dsc-accordion-heading-background--hover: var(--ds-color-surface-hover); + --dsc-accordion-heading-background--open: var(--ds-color-surface-default); + --dsc-accordion-heading-background: var(--ds-color-surface-default); + --dsc-accordion-border-color: var(--ds-color-border-subtle); + } + + &[data-color='neutral'] { + --dsc-accordion-background: var(--ds-color-background-default); + --dsc-accordion-heading-background--hover: var(--ds-color-surface-default); + --dsc-accordion-heading-background--open: var(--ds-color-background-subtle); + --dsc-accordion-heading-background: var(--ds-color-background-default); + } + + &[data-color='subtle'] { + --dsc-accordion-background: var(--ds-color-neutral-background-subtle); + --dsc-accordion-heading-background--hover: var(--ds-color-neutral-surface-hover); + --dsc-accordion-heading-background--open: var(--ds-color-neutral-surface-default); + --dsc-accordion-heading-background: var(--ds-color-neutral-background-subtle); + } + + --dsc-accordion-border: 1px solid var(--dsc-accordion-border-color); --dsc-accordion-border-radius: var(--ds-border-radius-md); - --dsc-accordion-border: 1px solid var(--ds-color-neutral-border-subtle); --dsc-accordion-chevron-gap: var(--ds-spacing-2); --dsc-accordion-chevron-size: var(--ds-spacing-6); --dsc-accordion-chevron-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5.97 9.47a.75.75 0 0 1 1.06 0L12 14.44l4.97-4.97a.75.75 0 1 1 1.06 1.06l-5.5 5.5a.75.75 0 0 1-1.06 0l-5.5-5.5a.75.75 0 0 1 0-1.06'/%3E%3C/svg%3E"); - --dsc-accordion-heading-background--hover: var(--ds-color-neutral-surface-default); - --dsc-accordion-heading-background--open: var(--ds-color-neutral-background-subtle); - --dsc-accordion-heading-background: var(--ds-color-neutral-background-default); + --dsc-accordion-padding: var(--ds-spacing-2) var(--ds-spacing-4); --dsc-accordion-size: var(--ds-sizing-14); @@ -26,38 +52,6 @@ border-bottom-right-radius: var(--dsc-accordion-border-radius); } } - - &[data-color='subtle'] { - --dsc-accordion-background: var(--ds-color-neutral-background-subtle); - --dsc-accordion-border: 1px solid var(--ds-color-neutral-border-subtle); - --dsc-accordion-heading-background--hover: var(--ds-color-neutral-surface-hover); - --dsc-accordion-heading-background--open: var(--ds-color-neutral-surface-default); - --dsc-accordion-heading-background: var(--ds-color-neutral-background-subtle); - } - - &[data-color='brand1'] { - --dsc-accordion-background: var(--ds-color-brand1-background-subtle); - --dsc-accordion-border: 1px solid var(--ds-color-brand1-border-subtle); - --dsc-accordion-heading-background--hover: var(--ds-color-brand1-surface-hover); - --dsc-accordion-heading-background--open: var(--ds-color-brand1-surface-default); - --dsc-accordion-heading-background: var(--ds-color-brand1-surface-default); - } - - &[data-color='brand2'] { - --dsc-accordion-background: var(--ds-color-brand2-background-subtle); - --dsc-accordion-border: 1px solid var(--ds-color-brand2-border-subtle); - --dsc-accordion-heading-background--hover: var(--ds-color-brand2-surface-hover); - --dsc-accordion-heading-background--open: var(--ds-color-brand2-surface-default); - --dsc-accordion-heading-background: var(--ds-color-brand2-surface-default); - } - - &[data-color='brand3'] { - --dsc-accordion-background: var(--ds-color-brand3-background-subtle); - --dsc-accordion-border: 1px solid var(--ds-color-brand3-border-subtle); - --dsc-accordion-heading-background--hover: var(--ds-color-brand3-surface-hover); - --dsc-accordion-heading-background--open: var(--ds-color-brand3-surface-default); - --dsc-accordion-heading-background: var(--ds-color-brand3-surface-default); - } } .ds-accordion__item { diff --git a/packages/css/alert.css b/packages/css/alert.css index fe28d6aaae..2ded060943 100644 --- a/packages/css/alert.css +++ b/packages/css/alert.css @@ -1,12 +1,14 @@ .ds-alert { + /* default color/variant: info */ --dsc-alert-background: var(--ds-color-info-surface-default); --dsc-alert-border-color: var(--ds-color-info-border-default); + --dsc-alert-icon-color: var(--ds-color-info-text-subtle); + --dsc-alert-icon-url: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' d='M3.25 4A.75.75 0 0 1 4 3.25h16a.75.75 0 0 1 .75.75v16a.75.75 0 0 1-.75.75H4a.75.75 0 0 1-.75-.75zM11 7.75a1 1 0 1 1 2 0 1 1 0 0 1-2 0m-1.25 3a.75.75 0 0 1 .75-.75H12a.75.75 0 0 1 .75.75v4.75h.75a.75.75 0 0 1 0 1.5h-3a.75.75 0 0 1 0-1.5h.75v-4h-.75a.75.75 0 0 1-.75-.75'/%3E%3C/svg%3E"); + --dsc-alert-border-radius: var(--ds-border-radius-md); --dsc-alert-color: var(--ds-color-neutral-text-default); --dsc-alert-gap: var(--ds-spacing-2); - --dsc-alert-icon-color: var(--ds-color-info-text-subtle); --dsc-alert-icon-size: var(--ds-sizing-7); - --dsc-alert-icon-url: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' d='M3.25 4A.75.75 0 0 1 4 3.25h16a.75.75 0 0 1 .75.75v16a.75.75 0 0 1-.75.75H4a.75.75 0 0 1-.75-.75zM11 7.75a1 1 0 1 1 2 0 1 1 0 0 1-2 0m-1.25 3a.75.75 0 0 1 .75-.75H12a.75.75 0 0 1 .75.75v4.75h.75a.75.75 0 0 1 0 1.5h-3a.75.75 0 0 1 0-1.5h.75v-4h-.75a.75.75 0 0 1-.75-.75'/%3E%3C/svg%3E"); --dsc-alert-padding: var(--ds-spacing-6); background: var(--dsc-alert-background); @@ -33,24 +35,20 @@ /** * Colors */ + &[data-color] { + --dsc-alert-background: var(--ds-color-surface-default); + --dsc-alert-border-color: var(--ds-color-border-default); + --dsc-alert-icon-color: var(--ds-color-text-subtle); + } &[data-color='warning'] { - --dsc-alert-border-color: var(--ds-color-warning-border-default); - --dsc-alert-icon-color: var(--ds-color-warning-text-subtle); - --dsc-alert-background: var(--ds-color-warning-surface-default); --dsc-alert-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2.25a.75.75 0 0 1 .66.39l9.52 17.25a.75.75 0 0 1-.65 1.11H2.47a.75.75 0 0 1-.65-1.11l9.52-17.25a.75.75 0 0 1 .66-.39m0 6.5a.75.75 0 0 1 .75.75v4a.75.75 0 0 1-1.5 0v-4a.75.75 0 0 1 .75-.75m-1 7.75a1 1 0 1 1 2 0 1 1 0 0 1-2 0'/%3E%3C/svg%3E"); } &[data-color='success'] { - --dsc-alert-background: var(--ds-color-success-surface-default); - --dsc-alert-border-color: var(--ds-color-success-border-default); - --dsc-alert-icon-color: var(--ds-color-success-text-subtle); --dsc-alert-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 21.75a9.75 9.75 0 1 0 0-19.5 9.75 9.75 0 0 0 0 19.5m4.95-12.47a.81.81 0 0 0-1.24-1.05l-5.39 6.36-2.62-2.62a.81.81 0 0 0-1.15 1.15l3.25 3.25a.81.81 0 0 0 1.2-.05z'/%3E%3C/svg%3E"); } &[data-color='danger'] { - --dsc-alert-background: var(--ds-color-danger-surface-default); - --dsc-alert-border-color: var(--ds-color-danger-border-default); - --dsc-alert-icon-color: var(--ds-color-danger-text-subtle); --dsc-alert-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M7.74 2.47a.75.75 0 0 1 .53-.22h7.46a.75.75 0 0 1 .53.22l5.27 5.27c.14.14.22.33.22.53v7.46a.75.75 0 0 1-.22.53l-5.27 5.27a.75.75 0 0 1-.53.22H8.27a.75.75 0 0 1-.53-.22l-5.27-5.27a.75.75 0 0 1-.22-.53V8.27a.75.75 0 0 1 .22-.53zm1.29 5.5a.75.75 0 0 0-1.06 1.06L10.94 12l-2.97 2.97a.75.75 0 1 0 1.06 1.06L12 13.06l2.97 2.97a.75.75 0 1 0 1.06-1.06L13.06 12l2.97-2.97a.75.75 0 0 0-1.06-1.06L12 10.94z'/%3E%3C/svg%3E"); } } diff --git a/packages/css/avatar.css b/packages/css/avatar.css index 3b0a06c20e..fad367a53c 100644 --- a/packages/css/avatar.css +++ b/packages/css/avatar.css @@ -1,7 +1,15 @@ .ds-avatar { - --dsc-avatar-background: var(--ds-color-accent-base-default); --dsc-avatar-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' fill='none' viewBox='0 0 24 24' focusable='false' role='img'%3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M8.25 7.5a3.75 3.75 0 1 1 7.5 0 3.75 3.75 0 0 1-7.5 0M12 2.25a5.25 5.25 0 1 0 0 10.5 5.25 5.25 0 0 0 0-10.5M8.288 17.288A5.25 5.25 0 0 1 17.25 21a.75.75 0 0 0 1.5 0 6.75 6.75 0 0 0-13.5 0 .75.75 0 0 0 1.5 0 5.25 5.25 0 0 1 1.538-3.712' clip-rule='evenodd'%3E%3C/path%3E%3C/svg%3E"); + + /* default color: accent */ + --dsc-avatar-background: var(--ds-color-accent-base-default); --dsc-avatar-color: var(--ds-color-accent-contrast-default); + + &[data-color] { + --dsc-avatar-background: var(--ds-color-base-default); + --dsc-avatar-color: var(--ds-color-contrast-default); + } + --dsc-avatar-size: var(--ds-sizing-12); --dsc-avatar-padding: var(--ds-spacing-2); --dsc-avatar-border-radius: var(--ds-border-radius-full); @@ -54,31 +62,6 @@ --dsc-avatar-border-radius: var(--ds-border-radius-sm); } - &[data-color='accent'] { - --dsc-avatar-background: var(--ds-color-accent-base-default); - --dsc-avatar-color: var(--ds-color-accent-contrast-default); - } - - &[data-color='neutral'] { - --dsc-avatar-background: var(--ds-color-neutral-base-default); - --dsc-avatar-color: var(--ds-color-neutral-contrast-default); - } - - &[data-color='brand1'] { - --dsc-avatar-background: var(--ds-color-brand1-base-default); - --dsc-avatar-color: var(--ds-color-brand1-contrast-default); - } - - &[data-color='brand2'] { - --dsc-avatar-background: var(--ds-color-brand2-base-default); - --dsc-avatar-color: var(--ds-color-brand2-contrast-default); - } - - &[data-color='brand3'] { - --dsc-avatar-background: var(--ds-color-brand3-base-default); - --dsc-avatar-color: var(--ds-color-brand3-contrast-default); - } - &[data-size='xs'] { font-size: var(--ds-font-size-1); } diff --git a/packages/css/badge.css b/packages/css/badge.css index a36ea5d103..fdf670f0e4 100644 --- a/packages/css/badge.css +++ b/packages/css/badge.css @@ -1,6 +1,14 @@ .ds-badge { - --dsc-badge-background: var(--ds-color-accent-base-default); - --dsc-badge-color: var(--ds-color-accent-contrast-default); + /* color vars */ + --dsc-badge-background: var(--ds-color-base-default); + --dsc-badge-color: var(--ds-color-contrast-default); + + &[data-color='neutral'] { + --dsc-badge-background: var(--ds-color-neutral-surface-default); + --dsc-badge-color: var(--ds-color-neutral-text-default); + } + + /* sizing vars */ --dsc-badge-padding: 0 calc(var(--ds-spacing-1) + calc(var(--ds-spacing-1) / 2)); --dsc-badge-size: calc(var(--ds-sizing-3) + calc(var(--ds-spacing-1) / 2)); @@ -28,31 +36,6 @@ font-size: 1.25em; /* Auto scale icon based on font-size */ } - &[data-color='info'] { - --dsc-badge-background: var(--ds-color-info-base-default); - --dsc-badge-color: var(--ds-color-info-contrast-default); - } - - &[data-color='success'] { - --dsc-badge-background: var(--ds-color-success-base-default); - --dsc-badge-color: var(--ds-color-success-contrast-default); - } - - &[data-color='warning'] { - --dsc-badge-background: var(--ds-color-warning-base-default); - --dsc-badge-color: var(--ds-color-warning-contrast-default); - } - - &[data-color='danger'] { - --dsc-badge-background: var(--ds-color-danger-base-default); - --dsc-badge-color: var(--ds-color-danger-contrast-default); - } - - &[data-color='neutral'] { - --dsc-badge-background: var(--ds-color-neutral-surface-default); - --dsc-badge-color: var(--ds-color-neutral-text-default); - } - /* If placement is present, we are floating */ &[data-placement]::before { position: absolute; diff --git a/packages/css/button.css b/packages/css/button.css index ce70cc8584..a814697d2c 100644 --- a/packages/css/button.css +++ b/packages/css/button.css @@ -1,8 +1,40 @@ .ds-button { + /* default color: accent */ --dsc-button-background--active: var(--ds-color-accent-base-active); --dsc-button-background--hover: var(--ds-color-accent-base-hover); --dsc-button-background: var(--ds-color-accent-base-default); --dsc-button-color: var(--ds-color-accent-contrast-default); + --dsc-button-border-color: transparent; + + &[data-variant='secondary'], + &[data-variant='tertiary'] { + --dsc-button-background: transparent; + --dsc-button-background--active: var(--ds-color-accent-surface-hover); + --dsc-button-background--hover: var(--ds-color-accent-surface-default); + --dsc-button-color: var(--ds-color-accent-text-subtle); + } + &[data-variant='secondary'] { + --dsc-button-border-color: var(--ds-color-accent-border-strong); + } + + &[data-color] { + --dsc-button-background--active: var(--ds-color-base-active); + --dsc-button-background--hover: var(--ds-color-base-hover); + --dsc-button-background: var(--ds-color-base-default); + --dsc-button-color: var(--ds-color-contrast-default); + + &[data-variant='secondary'], + &[data-variant='tertiary'] { + --dsc-button-background: transparent; + --dsc-button-background--active: var(--ds-color-surface-hover); + --dsc-button-background--hover: var(--ds-color-surface-default); + --dsc-button-color: var(--ds-color-text-subtle); + } + &[data-variant='secondary'] { + --dsc-button-border-color: var(--ds-color-border-strong); + } + } + --dsc-button-font-size: var(--ds-font-size-5); --dsc-button-gap: var(--ds-spacing-2); --dsc-button-padding: var(--ds-spacing-2) var(--ds-spacing-4); @@ -11,7 +43,7 @@ align-items: center; background: var(--dsc-button-background); border-radius: var(--ds-border-radius-default); - border: var(--ds-border-width-default) solid var(--dsc-button-border-color, transparent); + border: var(--ds-border-width-default) solid var(--dsc-button-border-color); box-sizing: border-box; color: var(--dsc-button-color); cursor: pointer; @@ -57,84 +89,6 @@ text-align: center; } - /** - * Variants - */ - &[data-variant='secondary'] { - --dsc-button-background--active: var(--ds-color-accent-surface-hover); - --dsc-button-background--hover: var(--ds-color-accent-surface-default); - --dsc-button-background: transparent; - --dsc-button-border-color: var(--ds-color-accent-border-strong); - --dsc-button-color--active: var(--ds-color-accent-text-default); - --dsc-button-color--hover: var(--ds-color-accent-text-default); - --dsc-button-color: var(--ds-color-accent-text-subtle); - } - - &[data-variant='tertiary'] { - --dsc-button-background--active: var(--ds-color-accent-surface-hover); - --dsc-button-background--hover: var(--ds-color-accent-surface-default); - --dsc-button-background: transparent; - --dsc-button-border-color: var(--dsc-button-tertiary-border-color); - --dsc-button-color--active: var(--ds-color-accent-text-default); - --dsc-button-color--hover: var(--ds-color-accent-text-default); - --dsc-button-color: var(--ds-color-accent-text-subtle); - } - - /** - * Colors - */ - &[data-color='neutral'] { - --dsc-button-background--active: var(--ds-color-neutral-base-active); - --dsc-button-background--hover: var(--ds-color-neutral-base-hover); - --dsc-button-background: var(--ds-color-neutral-base-default); - --dsc-button-color: var(--ds-color-neutral-contrast-default); - } - - &[data-color='neutral'][data-variant='secondary'] { - --dsc-button-background--active: var(--ds-color-neutral-surface-hover); - --dsc-button-background--hover: var(--ds-color-neutral-surface-default); - --dsc-button-background: transparent; - --dsc-button-border-color: var(--ds-color-neutral-border-strong); - --dsc-button-color--active: var(--ds-color-neutral-text-default); - --dsc-button-color--hover: var(--ds-color-neutral-text-default); - --dsc-button-color: var(--ds-color-neutral-text-subtle); - } - - &[data-color='neutral'][data-variant='tertiary'] { - --dsc-button-background--active: var(--ds-color-neutral-surface-hover); - --dsc-button-background--hover: var(--ds-color-neutral-surface-default); - --dsc-button-background: transparent; - --dsc-button-color--active: var(--ds-color-neutral-text-default); - --dsc-button-color--hover: var(--ds-color-neutral-text-default); - --dsc-button-color: var(--ds-color-neutral-text-subtle); - } - - &[data-color='danger'] { - --dsc-button-background--active: var(--ds-color-danger-base-active); - --dsc-button-background--hover: var(--ds-color-danger-base-hover); - --dsc-button-background: var(--ds-color-danger-base-default); - --dsc-button-color: var(--ds-color-danger-contrast-default); - } - - &[data-color='danger'][data-variant='secondary'] { - --dsc-button-background--active: var(--ds-color-danger-surface-hover); - --dsc-button-background--hover: var(--ds-color-danger-surface-default); - --dsc-button-background: transparent; - --dsc-button-border-color: var(--ds-color-danger-border-strong); - --dsc-button-color--active: var(--ds-color-danger-text-default); - --dsc-button-color--hover: var(--ds-color-danger-text-default); - --dsc-button-color: var(--ds-color-danger-text-subtle); - } - - &[data-color='danger'][data-variant='tertiary'] { - --dsc-button-background--active: var(--ds-color-danger-surface-hover); - --dsc-button-background--hover: var(--ds-color-danger-surface-default); - --dsc-button-background: transparent; - --dsc-button-color--active: var(--ds-color-danger-text-default); - --dsc-button-color--hover: var(--ds-color-danger-text-default); - --dsc-button-color: var(--ds-color-danger-text-subtle); - } - /** * States */ diff --git a/packages/css/card.css b/packages/css/card.css index 51ea19eaee..e0cee500e4 100644 --- a/packages/css/card.css +++ b/packages/css/card.css @@ -1,10 +1,26 @@ .ds-card { + /* default color: neutral */ --dsc-card-background--active: var(--ds-color-neutral-surface-default); --dsc-card-background--hover: var(--ds-color-neutral-background-subtle); --dsc-card-background: var(--ds-color-neutral-background-default); --dsc-card-border-color: var(--ds-color-neutral-border-subtle); - --dsc-card-border: 1px solid var(--dsc-card-border-color); --dsc-card-color: var(--ds-color-neutral-text-default); + + &[data-color]:where(:not([data-color='subtle'])) { + --dsc-card-background--active: var(--ds-color-surface-default); + --dsc-card-background--hover: var(--ds-color-background-subtle); + --dsc-card-background: var(--ds-color-surface-default); + --dsc-card-border-color: var(--ds-color-border-subtle); + --dsc-card-color: var(--ds-color-text-default); + } + &[data-color='neutral'] { + --dsc-card-background: var(--ds-color-background-default); + } + &[data-color='subtle'] { + --dsc-card-background: var(--ds-color-neutral-background-subtle); + } + + --dsc-card-border: 1px solid var(--dsc-card-border-color); --dsc-card-gap: var(--ds-spacing-3); --dsc-card-padding: var(--ds-spacing-6); @@ -58,37 +74,6 @@ &:has(> .ds-card__block) { padding: 0; /* Let Card.Block own the padding */ } - - &[data-color='subtle'] { - --dsc-card-background--active: var(--ds-color-neutral-surface-hover); - --dsc-card-background--hover: var(--ds-color-neutral-surface-default); - --dsc-card-background: var(--ds-color-neutral-background-subtle); - --dsc-card-border-color: var(--ds-color-neutral-border-subtle); - } - - &[data-color='brand1'] { - --dsc-card-background--active: var(--ds-color-brand1-surface-active); - --dsc-card-background--hover: var(--ds-color-brand1-surface-hover); - --dsc-card-background: var(--ds-color-brand1-surface-default); - --dsc-card-border-color: var(--ds-color-brand1-border-subtle); - --dsc-card-color: var(--ds-color-brand1-text-default); - } - - &[data-color='brand2'] { - --dsc-card-background--active: var(--ds-color-brand2-surface-active); - --dsc-card-background--hover: var(--ds-color-brand2-surface-hover); - --dsc-card-background: var(--ds-color-brand2-surface-default); - --dsc-card-border-color: var(--ds-color-brand2-border-subtle); - --dsc-card-color: var(--ds-color-brand2-text-default); - } - - &[data-color='brand3'] { - --dsc-card-background--active: var(--ds-color-brand3-surface-active); - --dsc-card-background--hover: var(--ds-color-brand3-surface-hover); - --dsc-card-background: var(--ds-color-brand3-surface-default); - --dsc-card-border-color: var(--ds-color-brand3-border-subtle); - --dsc-card-color: var(--ds-color-brand3-text-default); - } } /* Using :where to overwrite user agent CSS, but not our own CSS */ diff --git a/packages/css/chip.css b/packages/css/chip.css index 7b8733c06a..f94797a450 100644 --- a/packages/css/chip.css +++ b/packages/css/chip.css @@ -1,16 +1,16 @@ .ds-chip { - --dsc-chip-background: var(--ds-color-accent-surface-default); - --dsc-chip-background--hover: var(--ds-color-accent-surface-hover); - --dsc-chip-background--active: var(--ds-color-accent-surface-active); - --dsc-chip-background--checked: var(--ds-color-accent-base-default); - --dsc-chip-background--checked--hover: var(--ds-color-accent-base-hover); - --dsc-chip-background--checked--active: var(--ds-color-accent-base-active); - --dsc-chip-border-color: var(--ds-color-accent-border-subtle); + --dsc-chip-background: var(--ds-color-surface-default); + --dsc-chip-background--hover: var(--ds-color-surface-hover); + --dsc-chip-background--active: var(--ds-color-surface-active); + --dsc-chip-background--checked: var(--ds-color-base-default); + --dsc-chip-background--checked--hover: var(--ds-color-base-hover); + --dsc-chip-background--checked--active: var(--ds-color-base-active); + --dsc-chip-border-color: var(--ds-color-border-subtle); --dsc-chip-border-color--checked: transparent; - --dsc-chip-color: var(--ds-color-accent-text-default); - --dsc-chip-color--checked: var(--ds-color-accent-contrast-default); - --dsc-chip-input-color: var(--ds-color-accent-border-strong); - --dsc-chip-input-color--checked: var(--ds-color-accent-base-default); + --dsc-chip-color: var(--ds-color-text-default); + --dsc-chip-color--checked: var(--ds-color-contrast-default); + --dsc-chip-input-color: var(--ds-color-border-strong); + --dsc-chip-input-color--checked: var(--ds-color-base-default); --dsc-chip-border-radius: var(--ds-border-radius-full); --dsc-chip-height: var(--ds-sizing-8); diff --git a/packages/css/combobox.css b/packages/css/combobox.css index 06459c9ce8..93741d6b84 100644 --- a/packages/css/combobox.css +++ b/packages/css/combobox.css @@ -93,8 +93,8 @@ } .ds-combobox__input__wrapper.ds-combobox--readonly { - background: var(--ds-color-accent-base-default); - border-color: var(--ds-color-accent-base-default); + background: var(--ds-color-base-default); + border-color: var(--ds-color-base-default); } .ds-combobox__label { @@ -216,8 +216,8 @@ } .ds-combobox__option.ds-combobox__option--active { - background: var(--ds-color-accent-background-subtle); - border-left: 5px solid var(--ds-color-accent-base-default); + background: var(--ds-color-background-subtle); + border-left: 5px solid var(--ds-color-base-default); } .ds-combobox__option > div { @@ -240,7 +240,7 @@ } .ds-combobox__option--active .ds-combobox__option__label { - color: var(--ds-color-accent-text-default); + color: var(--ds-color-text-default); } .ds-combobox__option__icon-wrapper { @@ -254,7 +254,7 @@ } .ds-combobox__option--active .ds-combobox__option__icon-wrapper { - border-color: var(--ds-color-accent-base-default); + border-color: var(--ds-color-base-default); } .ds-combobox--sm .ds-combobox__option .ds-combobox__option__icon-wrapper { @@ -270,8 +270,8 @@ } .ds-combobox__option__icon-wrapper.ds-combobox__option__icon-wrapper--selected { - border-color: var(--ds-color-accent-base-default); - background-color: var(--ds-color-accent-base-default); + border-color: var(--ds-color-base-default); + background-color: var(--ds-color-base-default); } .ds-combobox__option__icon-wrapper__icon { @@ -287,8 +287,8 @@ } .ds-combobox__option--active .ds-combobox__option__icon-wrapper__icon { - stroke: var(--ds-color-accent-text-default); - color: var(--ds-color-accent-text-default); + stroke: var(--ds-color-text-default); + color: var(--ds-color-text-default); } .ds-combobox__option__icon-wrapper.ds-combobox__option__icon-wrapper--selected .ds-combobox__option__icon-wrapper__icon { diff --git a/packages/css/input.css b/packages/css/input.css index 003616e32b..6d30495f3e 100644 --- a/packages/css/input.css +++ b/packages/css/input.css @@ -4,13 +4,13 @@ --dsc-input-background--readonly: var(--ds-color-neutral-background-subtle); --dsc-input-background--switch: var(--ds-color-neutral-border-default); --dsc-input-background: var(--ds-color-neutral-background-default); - --dsc-input-border-color--checked: var(--ds-color-accent-base-default); + --dsc-input-border-color--checked: var(--ds-color-base-default); --dsc-input-border-color--invalid: var(--ds-color-danger-border-strong); --dsc-input-border-color--readonly: var(--ds-color-neutral-border-subtle); --dsc-input-border-color: var(--ds-color-neutral-border-default); --dsc-input-border-width--toggle: max(1px, calc(var(--ds-spacing-1) / 2)); /* Allow border-width to grow with font-size */ --dsc-input-border-width: 1px; - --dsc-input-color--checked: var(--ds-color-accent-contrast-default); + --dsc-input-color--checked: var(--ds-color-contrast-default); --dsc-input-color--invalid: var(--ds-color-danger-contrast-default); --dsc-input-color--readonly: var(--ds-color-neutral-text-subtle); --dsc-input-color: var(--ds-color-neutral-text-default); diff --git a/packages/css/link.css b/packages/css/link.css index 713a6b94ab..aced66a877 100644 --- a/packages/css/link.css +++ b/packages/css/link.css @@ -1,11 +1,13 @@ .ds-link { + /* default color: accent */ --dsc-link-background--active: var(--ds-color-accent-surface-default); - --dsc-link-background--focus: var(--ds-color-focus-outer); --dsc-link-color--active: var(--ds-color-accent-text-default); - --dsc-link-color--focus: var(--ds-color-focus-inner); --dsc-link-color--hover: var(--ds-color-accent-text-default); - --dsc-link-color--visited: var(--ds-global-purple-12); --dsc-link-color: var(--ds-color-accent-text-subtle); + + --dsc-link-background--focus: var(--ds-color-focus-outer); + --dsc-link-color--focus: var(--ds-color-focus-inner); + --dsc-link-color--visited: var(--ds-global-purple-12); --dsc-link-text-decoration-thickness--hover: 0.125em; /* 2px ish */ --dsc-link-text-decoration-thickness: 0.0625em; /* 1px ish */ @@ -22,11 +24,15 @@ /** * Colors */ + &[data-color] { + --dsc-link-background--active: var(--ds-color-surface-default); + --dsc-link-color--active: var(--ds-color-text-default); + --dsc-link-color--hover: var(--ds-color-text-default); + --dsc-link-color: var(--ds-color-text-subtle); + } &[data-color='neutral'] { - --dsc-link-background--active: var(--ds-color-neutral-surface-default); --dsc-link-color--active: var(--ds-color-neutral-text-subtle); --dsc-link-color--hover: var(--ds-color-neutral-text-subtle); - --dsc-link-color--visited: var(--ds-global-purple-12); --dsc-link-color: var(--ds-color-neutral-text-default); } diff --git a/packages/css/popover.css b/packages/css/popover.css index f826be7c35..8a5cb003c8 100644 --- a/packages/css/popover.css +++ b/packages/css/popover.css @@ -1,9 +1,10 @@ .ds-popover { - --dsc-popover-arrow-size: var(--ds-spacing-2); --dsc-popover-background: var(--ds-color-neutral-background-default); - --dsc-popover-border-radius: var(--ds-border-radius-md); - --dsc-popover-border: 1px solid var(--ds-color-neutral-border-default); + --dsc-popover-border-color: var(--ds-color-neutral-border-default); --dsc-popover-color: var(--ds-color-neutral-text-default); + --dsc-popover-arrow-size: var(--ds-spacing-2); + --dsc-popover-border-radius: var(--ds-border-radius-md); + --dsc-popover-border: 1px solid var(--dsc-popover-border-color); --dsc-popover-max-width: 300px; --dsc-popover-padding: var(--ds-spacing-3) var(--ds-spacing-4); @@ -49,21 +50,13 @@ rotate: -135deg; } - &[data-variant='info'] { - --dsc-popover-background: var(--ds-color-info-surface-default); - --dsc-popover-border: 1px solid var(--ds-color-info-border-default); - --dsc-popover-color: var(--ds-color-info-text-default); - } - - &[data-variant='warning'] { - --dsc-popover-background: var(--ds-color-warning-surface-default); - --dsc-popover-border: 1px solid var(--ds-color-warning-border-default); - --dsc-popover-color: var(--ds-color-warning-text-default); + &[data-color] { + --dsc-popover-background: var(--ds-color-surface-default); + --dsc-popover-border-color: var(--ds-color-border-default); + --dsc-popover-color: var(--ds-color-text-default); } - &[data-variant='danger'] { - --dsc-popover-background: var(--ds-color-danger-surface-default); - --dsc-popover-border: 1px solid var(--ds-color-danger-border-default); - --dsc-popover-color: var(--ds-color-danger-text-default); + &[data-color='neutral'] { + --dsc-popover-background: var(--ds-color-neutral-background-default); } } diff --git a/packages/css/skiplink.css b/packages/css/skiplink.css index d0f81725ec..ba653e334d 100644 --- a/packages/css/skiplink.css +++ b/packages/css/skiplink.css @@ -1,7 +1,7 @@ .ds-skiplink { --dsc-skiplink-padding: var(--ds-spacing-4); - --dsc-skiplink-background-color: var(--ds-color-accent-surface-hover); - --dsc-skiplink-color: var(--ds-color-accent-text-default); + --dsc-skiplink-background-color: var(--ds-color-surface-hover); + --dsc-skiplink-color: var(--ds-color-text-default); @composes ds-sr-only from './base/base.css'; } diff --git a/packages/css/spinner.css b/packages/css/spinner.css index 279cbe34ac..a251aaca3d 100644 --- a/packages/css/spinner.css +++ b/packages/css/spinner.css @@ -1,6 +1,7 @@ .ds-spinner { - --dsc-spinner-background: var(--ds-color-neutral-surface-default); - --dsc-spinner-stroke: var(--ds-color-neutral-border-default); + --dsc-spinner-background: var(--ds-color-surface-default); + --dsc-spinner-stroke: var(--ds-color-border-default); + --dsc-spinner-animation-duration: 2s; animation: ds-spinner-rotate-animation linear infinite var(--dsc-spinner-animation-duration); @@ -9,10 +10,6 @@ height: 1em; width: 1em; - &[data-color='accent'] { - --dsc-spinner-stroke: var(--ds-color-accent-base-default); - } - /* Using font-size to ensure consistent size when explicitly setting data-size */ &[data-size='2xs'] { font-size: 0.75rem; @@ -78,12 +75,10 @@ but don't remove it since it is not decorative. 50% { stroke-dasharray: 100px, 200px; stroke-dashoffset: -15px; - transform: rotate(0deg); } 100% { stroke-dasharray: 1px, 200px; stroke-dashoffset: -120px; - transform: rotate(15deg); } } diff --git a/packages/css/tabs.css b/packages/css/tabs.css index e74e55ecd7..a1bc10c341 100644 --- a/packages/css/tabs.css +++ b/packages/css/tabs.css @@ -1,5 +1,5 @@ .ds-tabs { - --dsc-tabs__tab-bottom-border-color: transparent; + --dsc-tabs-tab-bottom-border-color: transparent; --dsc-tabs-tab-padding: var(--ds-spacing-3) var(--ds-spacing-5); --dsc-tabs-tab-color: var(--ds-color-neutral-text-subtle); --dsc-tabs-content-padding: var(--ds-spacing-5); @@ -48,8 +48,8 @@ } &[aria-selected='true'] { - --dsc-tabs__tab-bottom-border-color: var(--ds-color-accent-base-default); - --dsc-tabs-tab-color: var(--ds-color-accent-text-subtle); + --dsc-tabs-tab-bottom-border-color: var(--ds-color-base-default); + --dsc-tabs-tab-color: var(--ds-color-text-subtle); } @composes ds-focus from './base/base.css'; @@ -64,7 +64,7 @@ display: block; height: .15em; /* Scale with font */ width: 100%; - background-color: var(--dsc-tabs__tab-bottom-border-color); + background-color: var(--dsc-tabs-tab-bottom-border-color); position: absolute; bottom: 0; left: 0; @@ -72,7 +72,7 @@ @media (hover: hover) and (pointer: fine) { &:hover:not([aria-selected='true']) { - --dsc-tabs__tab-bottom-border-color: var(--ds-color-neutral-border-subtle); + --dsc-tabs-tab-bottom-border-color: var(--ds-color-neutral-border-subtle); --dsc-tabs-tab-color: var(--ds-color-neutral-text-default); } } diff --git a/packages/css/tag.css b/packages/css/tag.css index 968aaa75c8..3080b3ee41 100644 --- a/packages/css/tag.css +++ b/packages/css/tag.css @@ -1,6 +1,11 @@ .ds-tag { --dsc-tag-background: var(--ds-color-neutral-surface-default); --dsc-tag-color: var(--ds-color-neutral-text-default); + + &[data-color] { + --dsc-tag-background: var(--ds-color-surface-default); + --dsc-tag-color: var(--ds-color-text-default); + } --dsc-tag-min-height: var(--ds-sizing-8); --dsc-tag-padding: 0 var(--ds-spacing-2); @@ -15,39 +20,4 @@ padding: var(--dsc-tag-padding); width: max-content; word-break: break-word; - - &[data-color='info'] { - --dsc-tag-background: var(--ds-color-info-surface-default); - --dsc-tag-color: var(--ds-color-info-text-default); - } - - &[data-color='success'] { - --dsc-tag-background: var(--ds-color-success-surface-default); - --dsc-tag-color: var(--ds-color-success-text-default); - } - - &[data-color='warning'] { - --dsc-tag-background: var(--ds-color-warning-surface-default); - --dsc-tag-color: var(--ds-color-warning-text-default); - } - - &[data-color='danger'] { - --dsc-tag-background: var(--ds-color-danger-surface-default); - --dsc-tag-color: var(--ds-color-danger-text-default); - } - - &[data-color='brand1'] { - --dsc-tag-background: var(--ds-color-brand1-surface-default); - --dsc-tag-color: var(--ds-color-brand1-text-default); - } - - &[data-color='brand2'] { - --dsc-tag-background: var(--ds-color-brand2-surface-default); - --dsc-tag-color: var(--ds-color-brand2-text-default); - } - - &[data-color='brand3'] { - --dsc-tag-background: var(--ds-color-brand3-surface-default); - --dsc-tag-color: var(--ds-color-brand3-text-default); - } } diff --git a/packages/css/textfield.css b/packages/css/textfield.css index ee09f6b7c5..c24af5a726 100644 --- a/packages/css/textfield.css +++ b/packages/css/textfield.css @@ -112,8 +112,8 @@ @media (hover: hover) and (pointer: fine) { .ds-textfield__input:not(:focus-visible, :disabled, [aria-disabled]):hover { - border-color: var(--ds-color-accent-border-strong); - box-shadow: inset 0 0 0 1px var(--ds-color-accent-border-strong); + border-color: var(--ds-color-border-strong); + box-shadow: inset 0 0 0 1px var(--ds-color-border-strong); } } diff --git a/packages/react/src/colors.ts b/packages/react/src/colors.ts new file mode 100644 index 0000000000..04eec91045 --- /dev/null +++ b/packages/react/src/colors.ts @@ -0,0 +1,5 @@ +export type SeverityColors = 'info' | 'success' | 'warning' | 'danger'; + +export type CustomColors = 'neutral' | (string & {}); + +export type Color = CustomColors | SeverityColors; diff --git a/packages/react/src/components/Accordion/Accordion.stories.tsx b/packages/react/src/components/Accordion/Accordion.stories.tsx index 62436b2f2e..fe42f5e542 100644 --- a/packages/react/src/components/Accordion/Accordion.stories.tsx +++ b/packages/react/src/components/Accordion/Accordion.stories.tsx @@ -40,7 +40,7 @@ export const Preview: StoryFn = (args) => ( ); export const AccordionBorder: StoryFn = () => ( - + Vedlegg Vedlegg 1, vedlegg 2, vedlegg 3 @@ -49,7 +49,7 @@ export const AccordionBorder: StoryFn = () => ( ); export const AccordionColor: StoryFn = () => ( - + Hvordan får jeg tildelt et jegernummer? @@ -135,7 +135,7 @@ export const AccordionColor: StoryFn = () => ( // Default values are selected in Controls Preview.args = { border: false, - color: 'neutral', + 'data-color': 'neutral', }; export const Controlled: StoryFn = () => { diff --git a/packages/react/src/components/Accordion/Accordion.tsx b/packages/react/src/components/Accordion/Accordion.tsx index bb737e4027..9d3019ed9d 100644 --- a/packages/react/src/components/Accordion/Accordion.tsx +++ b/packages/react/src/components/Accordion/Accordion.tsx @@ -1,28 +1,34 @@ import cl from 'clsx/lite'; import type { HTMLAttributes, ReactNode } from 'react'; import { forwardRef } from 'react'; +import type { Color } from '../../colors'; +import type { DefaultProps } from '../../types'; +import type { MergeRight } from '../../utilities'; -export type AccordionProps = { - /** - * Accordion background color - * @default neutral - */ - color?: 'brand1' | 'brand2' | 'brand3' | 'neutral' | 'subtle'; - /** - * Show border - * @default false - **/ - border?: boolean; - /** Instances of `Accordion.Item` */ - children: ReactNode; -} & HTMLAttributes; +export type AccordionProps = MergeRight< + DefaultProps & HTMLAttributes, + { + /** + * Accordion background color. + * @default neutral + */ + 'data-color'?: 'subtle' | Color; + /** + * Show border + * @default false + **/ + border?: boolean; + /** Instances of `Accordion.Item` */ + children: ReactNode; + } +>; /** * Accordion component, contains `Accordion.Item` components. */ export const Accordion = forwardRef( function Accordion( - { border = false, color = 'neutral', className, ...rest }, + { border = false, 'data-color': color = 'neutral', className, ...rest }, ref, ) { return ( diff --git a/packages/react/src/components/Alert/Alert.stories.tsx b/packages/react/src/components/Alert/Alert.stories.tsx index 6f81a84843..7ceb85ec44 100644 --- a/packages/react/src/components/Alert/Alert.stories.tsx +++ b/packages/react/src/components/Alert/Alert.stories.tsx @@ -19,13 +19,13 @@ export default meta; export const Preview: Story = (args) => ; Preview.args = { - color: 'info', + 'data-color': 'info', 'data-size': undefined, children: 'En beskjed det er viktig at brukeren ser', }; export const VariantInfo: Story = (args) => ( - + ( ); export const VariantSuccess: Story = (args) => ( - + ( ); export const VariantWarning: Story = (args) => ( - + ( ); export const VariantDanger: Story = (args) => ( - + ( ); export const MedKunHeading: Story = (args) => ( - + Du har 7 dager igjen på å fullføre søknaden. ); export const MedLenke: Story = (args) => ( - + ( ); export const UtenAria: Story = (args) => ( - + ( ); export const MedAria: Story = (args) => ( - + & - DefaultProps; +export type AlertProps = MergeRight< + DefaultProps & HTMLAttributes, + { + /** + * Sets color and icon. + * @default info + */ + 'data-color'?: SeverityColors; + } +>; /** * Alerts are used to inform users about important information, warnings, errors, or success. @@ -18,7 +22,7 @@ export type AlertProps = { * Dette er en informasjonsmelding */ export const Alert = forwardRef(function Alert( - { color = 'info', className, ...rest }, + { 'data-color': color = 'info', className, ...rest }, ref, ) { return ( diff --git a/packages/react/src/components/Avatar/Avatar.stories.tsx b/packages/react/src/components/Avatar/Avatar.stories.tsx index e0ad93fa3f..9899e3e5b1 100644 --- a/packages/react/src/components/Avatar/Avatar.stories.tsx +++ b/packages/react/src/components/Avatar/Avatar.stories.tsx @@ -27,7 +27,7 @@ export const Preview: Story = (args) => ; Preview.args = { 'aria-label': 'Ola Nordmann', - color: 'accent', + 'data-color': 'accent', 'data-size': 'md', variant: 'circle', children: '', @@ -51,11 +51,11 @@ export const Sizes: Story = () => ( export const ColorVariants: Story = () => ( <> - - - - - + + + + + ); @@ -90,7 +90,7 @@ export const InDropdown: Story = () => ( - + ON @@ -100,7 +100,11 @@ export const InDropdown: Story = () => ( - + Sogndal kommune diff --git a/packages/react/src/components/Avatar/Avatar.test.tsx b/packages/react/src/components/Avatar/Avatar.test.tsx index 5306d24d00..2a99f1f0f5 100644 --- a/packages/react/src/components/Avatar/Avatar.test.tsx +++ b/packages/react/src/components/Avatar/Avatar.test.tsx @@ -6,7 +6,6 @@ describe('Avatar', () => { render(); expect(screen.getByRole('img')).toBeInTheDocument(); expect(screen.getByRole('img')).toHaveAttribute('data-variant', 'circle'); - expect(screen.getByRole('img')).toHaveAttribute('data-color', 'accent'); }); it('should render correctly with custom props', () => { diff --git a/packages/react/src/components/Avatar/Avatar.tsx b/packages/react/src/components/Avatar/Avatar.tsx index da56eea341..c8de44ef55 100644 --- a/packages/react/src/components/Avatar/Avatar.tsx +++ b/packages/react/src/components/Avatar/Avatar.tsx @@ -2,39 +2,44 @@ import { Slot } from '@radix-ui/react-slot'; import cl from 'clsx/lite'; import { Fragment, forwardRef } from 'react'; import type { HTMLAttributes, ReactNode } from 'react'; +import type { Color } from '../../colors'; +import type { DefaultProps, Size } from '../../types'; +import type { MergeRight } from '../../utilities'; -export type AvatarProps = { - /** - * The name of the person the avatar represents. - */ - 'aria-label': string; - /** - * The color of the avatar. - * - * @default 'accent' - */ - color?: 'accent' | 'neutral' | 'brand1' | 'brand2' | 'brand3'; - /** - * The size of the avatar. - */ - 'data-size'?: 'xs' | 'sm' | 'md' | 'lg'; - /** - * The shape of the avatar. - * - * @default 'circle' - */ - variant?: 'circle' | 'square'; - /** - * Initials to display inside the avatar. - */ - initials?: string; - /** - * Image, icon or initials to display inside the avatar. - * - * Gets `aria-hidden="true"` - */ - children?: ReactNode; -} & Omit, 'aria-label'>; +export type AvatarProps = MergeRight< + DefaultProps & HTMLAttributes, + { + /** + * The name of the person the avatar represents. + */ + 'aria-label': string; + /** + * The color of the avatar. + * @default accent + */ + 'data-color'?: Color; + /** + * The size of the avatar. + */ + 'data-size'?: 'xs' | Size; + /** + * The shape of the avatar. + * + * @default 'circle' + */ + variant?: 'circle' | 'square'; + /** + * Initials to display inside the avatar. + */ + initials?: string; + /** + * Image, icon or initials to display inside the avatar. + * + * Gets `aria-hidden="true"` + */ + children?: ReactNode; + } +>; /** * Avatars are used to represent people or entities. @@ -55,7 +60,6 @@ export type AvatarProps = { export const Avatar = forwardRef(function Avatar( { 'aria-label': ariaLabel, - color = 'accent', variant = 'circle', className, children, @@ -72,7 +76,6 @@ export const Avatar = forwardRef(function Avatar( ref={ref} className={cl('ds-avatar', className)} data-variant={variant} - data-color={color} data-initials={initials} role='img' aria-label={ariaLabel} diff --git a/packages/react/src/components/Badge/Badge.stories.tsx b/packages/react/src/components/Badge/Badge.stories.tsx index c0bd448308..ac2533dd60 100644 --- a/packages/react/src/components/Badge/Badge.stories.tsx +++ b/packages/react/src/components/Badge/Badge.stories.tsx @@ -28,7 +28,7 @@ Preview.args = { 'data-size': 'md', count: 10, maxCount: 9, - color: 'accent', + 'data-color': 'accent', }; export const Floating: Story = (args) => ( @@ -38,19 +38,19 @@ export const Floating: Story = (args) => ( gap: 'var(--ds-spacing-6)', }} > - + - + - + - + - +
( }} /> - +
( }} /> - +
( }} /> - +
( }} > ( gap: 'var(--ds-spacing-4)', }} > - + - + - +
@@ -138,13 +138,13 @@ export const InTabs: Story = (args) => ( Favoritter - + Tab 2 Nylige - + content 1 @@ -161,17 +161,17 @@ export const InButton: Story = (args) => ( }} > diff --git a/packages/react/src/components/Badge/Badge.tsx b/packages/react/src/components/Badge/Badge.tsx index 77c0a00e2c..ea1509965f 100644 --- a/packages/react/src/components/Badge/Badge.tsx +++ b/packages/react/src/components/Badge/Badge.tsx @@ -1,40 +1,42 @@ import cl from 'clsx/lite'; import { type HTMLAttributes, type ReactNode, forwardRef } from 'react'; +import type { Color } from '../../colors'; import type { DefaultProps } from '../../types'; +import type { MergeRight } from '../../utilities'; -export type BadgeProps = { - /** - * The color of the badge - * - * @default accent - */ - color?: 'accent' | 'info' | 'success' | 'warning' | 'danger' | 'neutral'; - /** - * The number to display in the badge - */ - count?: number; - /** - * The maximum number to display in the badge, when the count exceeds this number, the badge will display `{max}+` - */ - maxCount?: number; - /** - * The placement of the badge - * - * @default top-right - */ - placement?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'; - /** - * Use when badge is floating to change the position of the badge - * - * @default rectangle - */ - overlap?: 'circle' | 'rectangle'; - /** - * The badge will float on top of the children - */ - children?: ReactNode; -} & HTMLAttributes & - DefaultProps; +export type BadgeProps = MergeRight< + DefaultProps & HTMLAttributes, + { + /** + * The color of the badge. If left unspecified, the color is inherited from the nearest ancestor with data-color. + */ + 'data-color'?: Color; + /** + * The number to display in the badge + */ + count?: number; + /** + * The maximum number to display in the badge, when the count exceeds this number, the badge will display `{max}+` + */ + maxCount?: number; + /** + * The placement of the badge + * + * @default top-right + */ + placement?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'; + /** + * Use when badge is floating to change the position of the badge + * + * @default rectangle + */ + overlap?: 'circle' | 'rectangle'; + /** + * The badge will float on top of the children + */ + children?: ReactNode; + } +>; /** * `Badge` is a non-interactive component for displaying status with or without numbers. @@ -54,7 +56,6 @@ export type BadgeProps = { export const Badge = forwardRef(function Badge( { className, - color = 'accent', count, maxCount, overlap = 'rectangle', @@ -66,7 +67,6 @@ export const Badge = forwardRef(function Badge( return ( maxCount ? `${maxCount}+` : count } diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx index d38e49c8d0..cebbe8b570 100644 --- a/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -1,15 +1,18 @@ import cl from 'clsx/lite'; import { type HTMLAttributes, forwardRef } from 'react'; import type { DefaultProps } from '../../types'; +import type { MergeRight } from '../../utilities'; -export type BreadcrumbsProps = { - /** - * Sets the screen reader label for the Breadcrumbs area - * @default 'Du er her' - */ - 'aria-label'?: string; -} & HTMLAttributes & - DefaultProps; +export type BreadcrumbsProps = MergeRight< + DefaultProps & HTMLAttributes, + { + /** + * Sets the screen reader label for the Breadcrumbs area + * @default 'Du er her' + */ + 'aria-label'?: string; + } +>; export const Breadcrumbs = forwardRef( ({ 'aria-label': ariaLabel = 'Du er her:', className, ...rest }, ref) => ( diff --git a/packages/react/src/components/Button/Button.stories.tsx b/packages/react/src/components/Button/Button.stories.tsx index 82c9a4bed0..008ebcb326 100644 --- a/packages/react/src/components/Button/Button.stories.tsx +++ b/packages/react/src/components/Button/Button.stories.tsx @@ -42,7 +42,7 @@ export const Preview: Story = { children: 'Knapp', disabled: false, variant: 'primary', - color: 'accent', + 'data-color': 'accent', 'data-size': 'md', icon: false, }, @@ -50,7 +50,7 @@ export const Preview: Story = { export const Primary: StoryFn = () => ( <> - @@ -58,7 +58,7 @@ export const Primary: StoryFn = () => ( export const Secondary: StoryFn = () => ( <> - @@ -66,7 +66,7 @@ export const Secondary: StoryFn = () => ( export const Tertiary: StoryFn = () => ( <> - @@ -75,13 +75,13 @@ export const Tertiary: StoryFn = () => ( export const Accent: StoryFn = () => ( <> - - - @@ -101,15 +101,15 @@ AccentPressed.parameters = { export const Neutral: StoryFn = () => ( <> - - - @@ -130,15 +130,15 @@ NeutralPressed.parameters = { export const Danger: StoryFn = () => ( <> - - - @@ -159,13 +159,13 @@ DangerPressed.parameters = { export const CombinedColors: StoryFn = () => ( <> - - - @@ -182,11 +182,11 @@ export const AsLink: StoryFn = () => ( export const TextAndIcon: StoryFn = () => ( <> - - @@ -236,19 +236,29 @@ export const Icons: StoryFn = () => ( export const IconOnly: StoryFn = () => ( <> - -