♻️(docs) various update for storybook
Color fixes due to new tokens architecture and few rewords.
This commit is contained in:
@@ -1,25 +1,26 @@
|
|||||||
<style>
|
<style>
|
||||||
code {
|
code {
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
border: 1px solid #EEEEEE;
|
border: 1px solid #eeeeee;
|
||||||
color: rgba(51,51,51,0.9);
|
color: rgba(51, 51, 51, 0.9);
|
||||||
background-color: #F8F8F8;
|
background-color: #f8f8f8;
|
||||||
}
|
}
|
||||||
/* Prevent the index.scss font-family to override code blocks's one. */
|
/* Prevent the index.scss font-family to override code blocks's one. */
|
||||||
pre * {
|
pre * {
|
||||||
font-family: ui-monospace,Menlo,Monaco,"Roboto Mono","Oxygen Mono","Ubuntu Monospace","Source Code Pro","Droid Sans Mono","Courier New",monospace;
|
font-family:
|
||||||
}
|
ui-monospace, Menlo, Monaco, "Roboto Mono", "Oxygen Mono",
|
||||||
|
"Ubuntu Monospace", "Source Code Pro", "Droid Sans Mono", "Courier New",
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
.cunningham-theme--dark {
|
.cunningham-theme--dark {
|
||||||
|
.prismjs {
|
||||||
|
background-color: var(--c--globals--colors--gray-800) !important;
|
||||||
.prismjs {
|
|
||||||
background-color: var(--c--globals--colors--gray-800) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
window.global = window;
|
window.global = window;
|
||||||
|
|||||||
@@ -1,68 +1,68 @@
|
|||||||
import { create } from '@storybook/theming';
|
import { create } from "@storybook/theming";
|
||||||
import { tokens } from '../src/cunningham-tokens';
|
import { tokens } from "../src/cunningham-tokens";
|
||||||
|
|
||||||
|
const buildTheme = (
|
||||||
|
colors: typeof tokens.themes.default.contextuals & any,
|
||||||
|
type: "default" | "dark" = "default"
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
brandUrl: "https://github.com/openfun/cunningham",
|
||||||
|
brandImage: "logo-cunningham.svg",
|
||||||
|
brandTitle: "Cunningham",
|
||||||
|
brandTarget: "_self",
|
||||||
|
|
||||||
const buildTheme = (colors: typeof tokens.themes.default.contextuals & any, type: 'default' | 'dark' = 'default') => {
|
//
|
||||||
return {
|
colorPrimary: colors.content.semantic.brand.primary, // content.brand.primary
|
||||||
brandUrl: 'https://github.com/openfun/cunningham',
|
colorSecondary: colors.content.semantic.brand.secondary, // content.brand.secondary
|
||||||
brandImage: 'logo-cunningham.svg',
|
|
||||||
brandTitle: 'Cunningham',
|
|
||||||
brandTarget: '_self',
|
|
||||||
|
|
||||||
//
|
// UI
|
||||||
colorPrimary: colors.content.semantic.brand.primary, // content.brand.primary
|
appBg: colors.background.surface.secondary, // background.surface.tertiary
|
||||||
colorSecondary: type === 'dark' ? colors.content.semantic.contextual.primary : colors.content.semantic.brand.secondary, // content.brand.secondary
|
appContentBg: colors.background.surface.tertiary, // background.surface.primary
|
||||||
|
appBorderColor: colors.border.surface.primary, // border.surface.primary
|
||||||
|
appBorderRadius: 4,
|
||||||
|
|
||||||
// UI
|
// Text colors
|
||||||
appBg: colors.background.surface.secondary, // background.surface.tertiary
|
textColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
||||||
appContentBg: colors.background.surface.tertiary, // background.surface.primary
|
textInverseColor: colors.content.semantic.neutral.secondary, // content.neutral.secondary
|
||||||
appBorderColor: colors.border.surface.primary, // border.surface.primary
|
|
||||||
appBorderRadius: 4,
|
|
||||||
|
|
||||||
// Text colors
|
// Toolbar default and active colors
|
||||||
textColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
barTextColor: colors.content.semantic.neutral.tertiary, // content.neutral.tertiary
|
||||||
textInverseColor: colors.content.semantic.neutral.secondary, // content.neutral.secondary
|
barSelectedColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
||||||
|
barBg: colors.background.surface.primary, // background.surface.primary
|
||||||
// Toolbar default and active colors
|
|
||||||
barTextColor: colors.content.semantic.neutral.tertiary, // content.neutral.tertiary
|
|
||||||
barSelectedColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
|
||||||
barBg: colors.background.surface.primary, // background.surface.primary
|
|
||||||
|
|
||||||
// Form colors
|
|
||||||
inputBg: colors.background.surface.primary, // background.surface.primary
|
|
||||||
inputBorder: colors.border.semantic.neutral.secondary, // border.neutral.secondary
|
|
||||||
inputTextColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
|
||||||
inputBorderRadius: 2,
|
|
||||||
|
|
||||||
// Code preview colors
|
|
||||||
codeBg: colors.background.surface.secondary, // background.surface.secondary
|
|
||||||
codeColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Form colors
|
||||||
|
inputBg: colors.background.surface.primary, // background.surface.primary
|
||||||
|
inputBorder: colors.border.semantic.neutral.secondary, // border.neutral.secondary
|
||||||
|
inputTextColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
||||||
|
inputBorderRadius: 2,
|
||||||
|
|
||||||
|
// Code preview colors
|
||||||
|
codeBg: colors.background.surface.secondary, // background.surface.secondary
|
||||||
|
codeColor: colors.content.semantic.neutral.primary, // content.neutral.primary
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const themes = {
|
export const themes = {
|
||||||
default: create({
|
default: create({
|
||||||
base: 'light',
|
base: "light",
|
||||||
...buildTheme(tokens.themes.default.contextuals),
|
...buildTheme(tokens.themes.default.contextuals),
|
||||||
}),
|
}),
|
||||||
dark: create({
|
dark: create({
|
||||||
base: 'dark',
|
base: "dark",
|
||||||
...buildTheme(tokens.themes.dark.contextuals, 'dark'),
|
...buildTheme(tokens.themes.dark.contextuals, "dark"),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum Themes {
|
export enum Themes {
|
||||||
dark = 'dark',
|
dark = "dark",
|
||||||
default = 'default'
|
default = "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BACKGROUND_COLOR_TO_THEME = {
|
export const BACKGROUND_COLOR_TO_THEME = {
|
||||||
'#181B24': Themes.dark,
|
"#181B24": Themes.dark,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getThemeFromGlobals = (globals: any): string => {
|
export const getThemeFromGlobals = (globals: any): string => {
|
||||||
const color = BACKGROUND_COLOR_TO_THEME[globals.backgrounds?.value];
|
const color = BACKGROUND_COLOR_TO_THEME[globals.backgrounds?.value];
|
||||||
return color ?? Themes.default;
|
return color ?? Themes.default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ export const OverflowLabels = () => {
|
|||||||
<Input label={"City" + overflow} fullWidth={true} />
|
<Input label={"City" + overflow} fullWidth={true} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="fs-l clr-gray-800 mb-t">Your curriculum vitae</div>
|
<div className="fs-l clr-content-semantic-neutral-primary mb-t">
|
||||||
|
Your curriculum vitae
|
||||||
|
</div>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
text="pdf only ( 4mb maximum )"
|
text="pdf only ( 4mb maximum )"
|
||||||
@@ -79,7 +81,7 @@ export const OverflowLabels = () => {
|
|||||||
<Button fullWidth={true}>Apply</Button>
|
<Button fullWidth={true}>Apply</Button>
|
||||||
<a
|
<a
|
||||||
href="/#"
|
href="/#"
|
||||||
className="clr-gray-800 fs-m"
|
className="clr-content-semantic-neutral-primary fs-m"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Need help ?
|
Need help ?
|
||||||
|
|||||||
@@ -84,9 +84,12 @@ export const Login = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button fullWidth={true}>Log in</Button>
|
<Button fullWidth={true}>Log in</Button>
|
||||||
<div className="fs-m clr-gray-800" style={{ textAlign: "center" }}>
|
<div
|
||||||
|
className="fs-m clr-content-semantic-neutral-primary"
|
||||||
|
style={{ textAlign: "center" }}
|
||||||
|
>
|
||||||
You do not have an account?{" "}
|
You do not have an account?{" "}
|
||||||
<a href="/#" className="clr-gray-800">
|
<a href="/#" className="clr-content-semantic-neutral-primary">
|
||||||
Register
|
Register
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -72,7 +72,9 @@ const SportsBase = ({ values }: SportProps) => {
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="fs-l clr-gray-800 mb-t">Gender</div>
|
<div className="fs-l clr-content-semantic-neutral-primary mb-t">
|
||||||
|
Gender
|
||||||
|
</div>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
state={getFieldState("gender", methods.formState)}
|
state={getFieldState("gender", methods.formState)}
|
||||||
text={getFieldErrorMessage("gender", methods.formState)}
|
text={getFieldErrorMessage("gender", methods.formState)}
|
||||||
@@ -158,7 +160,7 @@ const SportsBase = ({ values }: SportProps) => {
|
|||||||
<Button fullWidth={true}>Apply</Button>
|
<Button fullWidth={true}>Apply</Button>
|
||||||
<a
|
<a
|
||||||
href="/#"
|
href="/#"
|
||||||
className="clr-gray-800 fs-m"
|
className="clr-content-semantic-neutral-primary fs-m"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Need help ?
|
Need help ?
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const Login = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
className="fs-h3 fw-bold clr-gray-900"
|
className="fs-h3 fw-bold clr-content-semantic-neutral-primary"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Log in
|
Log in
|
||||||
@@ -43,9 +43,12 @@ export const Login = () => {
|
|||||||
<Checkbox label="Remember me" />
|
<Checkbox label="Remember me" />
|
||||||
</div>
|
</div>
|
||||||
<Button fullWidth={true}>Log in</Button>
|
<Button fullWidth={true}>Log in</Button>
|
||||||
<div className="fs-m clr-gray-800" style={{ textAlign: "center" }}>
|
<div
|
||||||
|
className="fs-m clr-content-semantic-neutral-primary"
|
||||||
|
style={{ textAlign: "center" }}
|
||||||
|
>
|
||||||
You do not have an account?{" "}
|
You do not have an account?{" "}
|
||||||
<a href="/#" className="clr-gray-800">
|
<a href="/#" className="clr-content-semantic-neutral-primary">
|
||||||
Register
|
Register
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,7 +68,7 @@ export const Application = () => {
|
|||||||
method="get"
|
method="get"
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
className="fs-h3 fw-bold clr-gray-900"
|
className="fs-h3 fw-bold clr-content-semantic-neutral-primary"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Application
|
Application
|
||||||
@@ -102,7 +105,9 @@ export const Application = () => {
|
|||||||
<Input label="City" fullWidth={true} />
|
<Input label="City" fullWidth={true} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="fs-l clr-gray-800 mb-t">Your curriculum vitae</div>
|
<div className="fs-l clr-content-semantic-neutral-primary mb-t">
|
||||||
|
Your curriculum vitae
|
||||||
|
</div>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
text="pdf only ( 4mb maximum )"
|
text="pdf only ( 4mb maximum )"
|
||||||
@@ -142,7 +147,7 @@ export const Application = () => {
|
|||||||
<Button fullWidth={true}>Apply</Button>
|
<Button fullWidth={true}>Apply</Button>
|
||||||
<a
|
<a
|
||||||
href="/#"
|
href="/#"
|
||||||
className="clr-gray-800 fs-m"
|
className="clr-content-semantic-neutral-primary fs-m"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Need help ?
|
Need help ?
|
||||||
@@ -163,13 +168,15 @@ export const Sports = () => {
|
|||||||
onSubmit={(e) => e.preventDefault()}
|
onSubmit={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
className="fs-h3 fw-bold clr-gray-900"
|
className="fs-h3 fw-bold clr-content-semantic-neutral-primary"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Register
|
Register
|
||||||
</h1>
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
<div className="fs-l clr-gray-800 mb-t">Gender</div>
|
<div className="fs-l clr-content-semantic-neutral-primary mb-t">
|
||||||
|
Gender
|
||||||
|
</div>
|
||||||
<div style={{ display: "flex", gap: "0.5rem" }}>
|
<div style={{ display: "flex", gap: "0.5rem" }}>
|
||||||
<Radio name="gender" label="Male" fullWidth={true} />
|
<Radio name="gender" label="Male" fullWidth={true} />
|
||||||
<Radio name="gender" label="Female" />
|
<Radio name="gender" label="Female" />
|
||||||
@@ -234,7 +241,7 @@ export const Sports = () => {
|
|||||||
<Button fullWidth={true}>Apply</Button>
|
<Button fullWidth={true}>Apply</Button>
|
||||||
<a
|
<a
|
||||||
href="/#"
|
href="/#"
|
||||||
className="clr-gray-800 fs-m"
|
className="clr-content-semantic-neutral-primary fs-m"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Need help ?
|
Need help ?
|
||||||
@@ -254,13 +261,15 @@ export const SportsDisabled = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
className="fs-h3 fw-bold clr-gray-900"
|
className="fs-h3 fw-bold clr-content-semantic-neutral-primary"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Register
|
Register
|
||||||
</h1>
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
<div className="fs-l clr-gray-800 mb-t">Gender</div>
|
<div className="fs-l clr-content-semantic-neutral-primary mb-t">
|
||||||
|
Gender
|
||||||
|
</div>
|
||||||
<div style={{ display: "flex", gap: "0.5rem" }}>
|
<div style={{ display: "flex", gap: "0.5rem" }}>
|
||||||
<Radio name="gender" label="Male" fullWidth={true} disabled={true} />
|
<Radio name="gender" label="Male" fullWidth={true} disabled={true} />
|
||||||
<Radio name="gender" label="Female" disabled={true} />
|
<Radio name="gender" label="Female" disabled={true} />
|
||||||
@@ -347,7 +356,7 @@ export const SportsDisabled = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<a
|
<a
|
||||||
href="/#"
|
href="/#"
|
||||||
className="clr-gray-800 fs-m"
|
className="clr-content-semantic-neutral-primary fs-m"
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
>
|
>
|
||||||
Need help ?
|
Need help ?
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ export const Default = {
|
|||||||
render: Template,
|
render: Template,
|
||||||
args: {
|
args: {
|
||||||
label: "Your label here",
|
label: "Your label here",
|
||||||
children: <span className="clr-gray-800">Hello world</span>,
|
children: (
|
||||||
|
<span className="clr-content-semantic-neutral-primary">Hello world</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,6 +46,8 @@ export const NoLabel = {
|
|||||||
args: {
|
args: {
|
||||||
label: "Your label here",
|
label: "Your label here",
|
||||||
hideLabel: true,
|
hideLabel: true,
|
||||||
children: <span className="clr-gray-800">Hello world</span>,
|
children: (
|
||||||
|
<span className="clr-content-semantic-neutral-primary">Hello world</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const BackgroundColors: Story = {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
flexBasis: "120px",
|
flexBasis: "200px",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
@@ -71,7 +71,7 @@ export const BackgroundColors: Story = {
|
|||||||
style={{ width: "72px", height: "48px" }}
|
style={{ width: "72px", height: "48px" }}
|
||||||
className={"bg-" + color + "-" + tint}
|
className={"bg-" + color + "-" + tint}
|
||||||
/>
|
/>
|
||||||
<pre className="clr-gray-800 fs-s mt-st">
|
<pre className="clr-content-semantic-neutral-primary fs-s mt-st">
|
||||||
bg-semantic-{color}-{tint}
|
bg-semantic-{color}-{tint}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { Canvas, Meta, Story, Source } from '@storybook/addon-docs';
|
import { Canvas, Meta, Story, Source } from "@storybook/addon-docs";
|
||||||
|
|
||||||
<Meta title="Migrating/From v3 to v4"/>
|
<Meta title="Migrating/From v3 to v4" />
|
||||||
|
|
||||||
# Objective
|
# Objective
|
||||||
|
|
||||||
The Cunningham color system has been revised with a methodical approach focused on modularity and accessibility. This revision enables the systematic generation of coherent themes, the automatic implementation of display modes (light/dark), the constant maintenance of contrast ratios independently of the selected theme, and the structured allocation of colors according to their role (background, content, border).
|
The Cunningham color system has been revised with a methodical approach focused on modularity and accessibility. This revision enables the systematic generation of coherent themes, the automatic implementation of display modes (light/dark), the constant maintenance of contrast ratios independently of the selected theme, and the structured allocation of colors according to their role (background, content, border).
|
||||||
|
|
||||||
An algorithm (available soon) has been developed to facilitate the rapid generation and adaptation of color palettes, producing a JSON file compatible with the existing infrastructure and natively supporting both light and dark display modes. This algorithmic solution optimizes Cunningham's adaptability to a wide range of user environments.
|
An algorithm (available soon) has been developed to facilitate the rapid generation and adaptation of color palettes, producing a JSON file compatible with the existing infrastructure and natively supporting both light and dark display modes. This algorithmic solution optimizes Cunningham's adaptability to a wide range of user environments.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Structure
|
# Structure
|
||||||
|
|
||||||
The color system is structured around two complementary, interdependent palettes:
|
The color system is structured around two complementary, interdependent palettes:
|
||||||
@@ -19,27 +17,37 @@ The color system is structured around two complementary, interdependent palettes
|
|||||||
|
|
||||||
This two-tier architecture facilitates the systematic propagation of changes: any alteration to the primitive palette is automatically reflected in all interface elements without the need for additional adjustments (see Figure 1).
|
This two-tier architecture facilitates the systematic propagation of changes: any alteration to the primitive palette is automatically reflected in all interface elements without the need for additional adjustments (see Figure 1).
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="docs/migrating/ressources/v3_to_v4/one.gif" alt="Changing the color primitive palette automatically updates the color contextual palette while maintaining contrast ratios inside components." />
|
<img
|
||||||
<figcaption style={{ textAlign: 'center' }}>Figure 1. Changing the color primitive palette automatically updates the color contextual palette while maintaining contrast ratios inside components.</figcaption>
|
src="docs/migrating/ressources/v3_to_v4/Figure1.gif"
|
||||||
|
alt="Changing the color primitive palette automatically updates the color contextual palette while maintaining contrast ratios inside components."
|
||||||
|
/>
|
||||||
|
<figcaption style={{ textAlign: "center" }}>
|
||||||
|
Figure 1. Changing the color primitive palette automatically updates the
|
||||||
|
color contextual palette while maintaining contrast ratios inside
|
||||||
|
components.
|
||||||
|
</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
A **theme** is a comprehensive color configuration derived from a brand's visual identity. It constitutes a specific implementation of the "Color Primitive" palette with predetermined color values. The Cunningham theme is provided as the default reference.
|
A **theme** is a comprehensive color configuration derived from a brand's visual identity. It constitutes a specific implementation of the "Color Primitive" palette with predetermined color values. The Cunningham theme is provided as the default reference.
|
||||||
|
|
||||||
A **mode** represents a systematic modulation of luminance and contrast parameters from an established theme, executed through the "Color Contextual" palette. This parametric adjustment accommodates diverse usage contexts while preserving the fundamental chromatic identity. Standard implementations include light and dark modes. These could be supplemented with specialized modes (such as high contrast) to meet specific accessibility needs or style requirements.
|
A **mode** represents a systematic modulation of luminance and contrast parameters from an established theme, executed through the "Color Contextual" palette. This parametric adjustment accommodates diverse usage contexts while preserving the fundamental chromatic identity. Standard implementations include light and dark modes. These could be supplemented with specialized modes (such as high contrast) to meet specific accessibility needs or style requirements.
|
||||||
|
|
||||||
|
|
||||||
# Palettes
|
# Palettes
|
||||||
|
|
||||||
## Color Primitive
|
## Color Primitive
|
||||||
|
|
||||||
The Color Primitive palette includes 16 colors. Each color is available in 19 distinct shades, except gray, which contains 22. Each shade is referenced by a number between 000 and 1000. A shade of 000 corresponds to pure white, while a shade of 1000 corresponds to pure black. Each intermediate shade value is then defined on the basis of the perceived luminance noted L* in the CIELAB color space ([more info on CIELAB](https://en.wikipedia.org/wiki/CIELAB_color_space)). Using L* as a reference ensures that each primitive color fits into a predictable tonal scale, regardless of hue or saturation. This ensures consistent contrast levels across all themes, promoting universal accessibility. The correspondence between shade (S) and perceived luminance (L*) is as follows: $S=1000 \times (1-L^*)$ with $L^* \in [0;1]$. See Figure 2 for an example.
|
The Color Primitive palette includes 16 colors. Each color is available in 19 distinct shades, except gray, which contains 22. Each shade is referenced by a number between 000 and 1000. A shade of 000 corresponds to pure white, while a shade of 1000 corresponds to pure black. Each intermediate shade value is then defined on the basis of the perceived luminance noted L* in the CIELAB color space ([more info on CIELAB](https://en.wikipedia.org/wiki/CIELAB_color_space)). Using L* as a reference ensures that each primitive color fits into a predictable tonal scale, regardless of hue or saturation. This ensures consistent contrast levels across all themes, promoting universal accessibility. The correspondence between shade (S) and perceived luminance (L*) is as follows: $S=1000 \times (1-L^*)$ with $L^* \in [0;1]$. See Figure 2 for an example.
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="docs/migrating/ressources/v3_to_v4/Figure2.png" alt="Breakdown of the first 6 colors of a Color Primitive theme. Gray includes 3 complementary shades." />
|
<img
|
||||||
<figcaption style={{ textAlign: 'center' }}>Figure 2. Breakdown of the first 6 colors of a "Color Primitive" theme. Gray includes 3 complementary shades.</figcaption>
|
src="docs/migrating/ressources/v3_to_v4/Figure2.png"
|
||||||
|
alt="Breakdown of the first 6 colors of a Color Primitive theme. Gray includes 3 complementary shades."
|
||||||
|
/>
|
||||||
|
<figcaption style={{ textAlign: "center" }}>
|
||||||
|
Figure 2. Breakdown of the first 6 colors of a "Color Primitive" theme. Gray
|
||||||
|
includes 3 complementary shades.
|
||||||
|
</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
To optimize visual ergonomics in dark mode, it is necessary to reduce color saturation in the darkest shades (S > 600). This gradual desaturation avoids perceptual glare in dark mode and improves legibility in low ambient light environments.
|
To optimize visual ergonomics in dark mode, it is necessary to reduce color saturation in the darkest shades (S > 600). This gradual desaturation avoids perceptual glare in dark mode and improves legibility in low ambient light environments.
|
||||||
@@ -49,17 +57,26 @@ An algorithm will soon be available to automatically adjust the parameters of pe
|
|||||||
## Color Contextual
|
## Color Contextual
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="docs/migrating/ressources/v3_to_v4/Figure3.png" alt="Excerpt from the Color Contextual palette for the background.semantic.brand role, with light and dark modes." />
|
<img
|
||||||
<figcaption style={{ textAlign: 'center' }}>Figure 3. excerpt from the "Color Contextual" palette for the background.semantic.brand role, with light and dark modes.</figcaption>
|
src="docs/migrating/ressources/v3_to_v4/Figure3.png"
|
||||||
|
alt="Excerpt from the Color Contextual palette for the background.semantic.brand role, with light and dark modes."
|
||||||
|
/>
|
||||||
|
<figcaption style={{ textAlign: "center" }}>
|
||||||
|
Figure 3. excerpt from the "Color Contextual" palette for the
|
||||||
|
background.semantic.brand role, with light and dark modes.
|
||||||
|
</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
The "Color Contextual" palette allows you to classify the various colors of a theme according to their actual use, differentiating between each of the modes (light, dark, etc.). This makes it possible (1) to simplify and limit errors when assigning colors to different components, and (2) to manage multiple modes.
|
The "Color Contextual" palette allows you to classify the various colors of a theme according to their actual use, differentiating between each of the modes (light, dark, etc.). This makes it possible (1) to simplify and limit errors when assigning colors to different components, and (2) to manage multiple modes.
|
||||||
|
|
||||||
This palette introduces the notion of roles, whose format is as follows:
|
This palette introduces the notion of roles, whose format is as follows:
|
||||||
|
|
||||||
<Source dark={true} code={`
|
<Source
|
||||||
|
dark={true}
|
||||||
|
code={`
|
||||||
layer.scope.(intent).variant.(state)
|
layer.scope.(intent).variant.(state)
|
||||||
`}></Source>
|
`}
|
||||||
|
></Source>
|
||||||
|
|
||||||
### Layer
|
### Layer
|
||||||
|
|
||||||
@@ -96,16 +113,14 @@ The state characterizes the interactional state of an interface element (e.g., H
|
|||||||
|
|
||||||
# **Accessibility**
|
# **Accessibility**
|
||||||
|
|
||||||
Accessibility is a fundamental principle guiding the design of this color system. As demonstrated in the Color Primitive section, the methodical standardization of luminance values (L*) ensures consistent contrast ratios, regardless of the theme or display mode selected. This systematic approach eliminates the need to re-evaluate accessibility parameters when implementing new themes, ensuring ongoing compliance with WCAG accessibility standards. The main objective is to establish an accessibility framework that is invariant and resilient in the face of multiple usage contexts.
|
Accessibility is a fundamental principle guiding the design of this color system. As demonstrated in the Color Primitive section, the methodical standardization of luminance values (L\*) ensures consistent contrast ratios, regardless of the theme or display mode selected. This systematic approach eliminates the need to re-evaluate accessibility parameters when implementing new themes, ensuring ongoing compliance with WCAG accessibility standards. The main objective is to establish an accessibility framework that is invariant and resilient in the face of multiple usage contexts.
|
||||||
|
|
||||||
# **Technical implementation**
|
# **Technical implementation**
|
||||||
|
|
||||||
Du à tout les changements, et à l'architecture des tokens, toutes les variables css ont changés. Vous devez donc mettre à jour vos variables css. Il faut absolument
|
Due to all the changes and the token architecture, all CSS variables have changed. You must therefore update your CSS variables. It is absolutely necessary to regenerate your tokens with the command `cunningham -g css,ts,js` to get the new CSS variables.
|
||||||
regénérer vos tokens avec la commande `cunningham -g css,ts,js` pour avoir les nouvelles variables css.
|
|
||||||
|
|
||||||
Dans le cas des tokens d'un theme, toutes les valeurs sémantiques sont dans le ```theme.globals```. Cela correspond aux couleurs primitives, transitions, fonts, spacings, breakpoints, font weights, font families, etc.
|
|
||||||
De fait toutes les valeurs contextuelles sont dans le ```theme.contextuals```. Cela correspond aux couleurs de fond, aux couleurs de contenu, aux couleurs de bordure, aux couleurs de palette, etc.
|
|
||||||
|
|
||||||
|
In the case of theme tokens, all primitive values are in `theme.globals`. This corresponds to primitive colors, transitions, fonts, spacings, breakpoints, font weights, font families, etc.
|
||||||
|
As a result, all contextual values are in `theme.contextuals`. This corresponds to background colors, content colors, border colors, palette colors, etc.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// Old structure
|
// Old structure
|
||||||
@@ -113,18 +128,17 @@ De fait toutes les valeurs contextuelles sont dans le ```theme.contextuals```. C
|
|||||||
"themes": {
|
"themes": {
|
||||||
"default": {
|
"default": {
|
||||||
"theme": {
|
"theme": {
|
||||||
"colors": "..."
|
"colors": "...",
|
||||||
"font": "..."
|
"font": "...",
|
||||||
"spacings": "..."
|
"spacings": "...",
|
||||||
"breakpoints": "..."
|
"breakpoints": "...",
|
||||||
"...": "..."
|
...
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"button": {
|
"button": {
|
||||||
"...": "..."
|
"...": "..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,38 +149,36 @@ De fait toutes les valeurs contextuelles sont dans le ```theme.contextuals```. C
|
|||||||
"themes": {
|
"themes": {
|
||||||
"default": {
|
"default": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"colors": "..."
|
"colors": "...",
|
||||||
"font": "..."
|
"font": "...",
|
||||||
"spacings": "..."
|
"spacings": "...",
|
||||||
"breakpoints": "..."
|
"breakpoints": "...",
|
||||||
"...": "..."
|
...
|
||||||
},
|
},
|
||||||
"contextuals": {
|
"contextuals": {
|
||||||
"background": {
|
"background": {
|
||||||
"...": "..."
|
"...": "..."
|
||||||
}
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"...": "..."
|
"...": "..."
|
||||||
}
|
},
|
||||||
"border": {
|
"border": {
|
||||||
"...": "..."
|
"...": "..."
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"button": {
|
"button": {
|
||||||
"...": "..."
|
"...": "..."
|
||||||
}
|
},
|
||||||
}
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## **Migration Steps**
|
## **Migration Steps**
|
||||||
|
|
||||||
|
|
||||||
### **1. Update Dependencies**
|
### **1. Update Dependencies**
|
||||||
|
|
||||||
First, update your `package.json` to use version 4.0.0:
|
First, update your `package.json` to use version 4.0.0:
|
||||||
@@ -196,7 +208,7 @@ All your references to old CSS variables will need to be updated. The new variab
|
|||||||
```css
|
```css
|
||||||
/* Old structure (v3) */
|
/* Old structure (v3) */
|
||||||
.test {
|
.test {
|
||||||
color: var(--c--theme--colors--primary-500)
|
color: var(--c--theme--colors--primary-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* New structure (v4) */
|
/* New structure (v4) */
|
||||||
@@ -207,13 +219,12 @@ All your references to old CSS variables will need to be updated. The new variab
|
|||||||
color: var(--c--contextuals--border--semantic--brand--primary);
|
color: var(--c--contextuals--border--semantic--brand--primary);
|
||||||
color: var(--c--contextuals--palette--brand--primary);
|
color: var(--c--contextuals--palette--brand--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### **3. Update Button Props**
|
### **3. Update Button Props**
|
||||||
|
|
||||||
Dans le cas du button, il faut mettre à jour les props `color` et `variant` pour utiliser le nouveau structure.
|
For buttons, you need to update the `color` and `variant` props to use the new structure.
|
||||||
La props color correspond au color primitive et la props variant au type de button.
|
The color prop corresponds to the primitive color and the variant prop corresponds to the button type.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
/* Old structure (v3) */
|
/* Old structure (v3) */
|
||||||
@@ -228,7 +239,7 @@ La props color correspond au color primitive et la props variant au type de butt
|
|||||||
|
|
||||||
### **4. Verify Accessibility**
|
### **4. Verify Accessibility**
|
||||||
|
|
||||||
The new architecture automatically ensures WCAG accessibility standards compliance through methodical standardization of luminance values (L*). Contrast ratios are now consistent regardless of the selected theme or display mode.
|
The new architecture automatically ensures WCAG accessibility standards compliance through methodical standardization of luminance values (L\*). Contrast ratios are now consistent regardless of the selected theme or display mode.
|
||||||
|
|
||||||
## **Migration Benefits**
|
## **Migration Benefits**
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const Default: Story = {
|
|||||||
// Trier les espacements par valeur croissante avant de les afficher
|
// Trier les espacements par valeur croissante avant de les afficher
|
||||||
// Sort spacings by value before rendering
|
// Sort spacings by value before rendering
|
||||||
const sortedSpacings = Object.entries(
|
const sortedSpacings = Object.entries(
|
||||||
tokens.themes.default.globals.spacings,
|
tokens.themes.default.globals.spacings
|
||||||
).sort((a, b) => {
|
).sort((a, b) => {
|
||||||
// On retire les éventuelles unités pour comparer numériquement
|
// On retire les éventuelles unités pour comparer numériquement
|
||||||
const parse = (v: any) =>
|
const parse = (v: any) =>
|
||||||
@@ -30,13 +30,13 @@ export const Default: Story = {
|
|||||||
style={{ display: "flex", alignItems: "center", gap: "10px" }}
|
style={{ display: "flex", alignItems: "center", gap: "10px" }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="fw-bold clr-content-neutral-primary"
|
className="fw-bold clr-content-semantic-neutral-primary"
|
||||||
style={{ width: "50px" }}
|
style={{ width: "50px" }}
|
||||||
>
|
>
|
||||||
-{key}
|
-{key}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="fw-medium fs-m clr-content-neutral-primary"
|
className="fw-medium fs-m clr-content-semantic-neutral-primary"
|
||||||
style={{ width: "100px" }}
|
style={{ width: "100px" }}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
@@ -56,11 +56,11 @@ export const Default: Story = {
|
|||||||
export const Example: Story = {
|
export const Example: Story = {
|
||||||
render: () => {
|
render: () => {
|
||||||
return (
|
return (
|
||||||
<div className="clr-gray-800 bg-error-tertiary">
|
<div className="clr-content-semantic-neutral-primary bg-yellow-500">
|
||||||
<div className="clr-gray-800 bg-brand-tertiary fw-medium p-t mb-l">
|
<div className="clr-content-semantic-neutral-primary bg-purple-500 fw-medium p-t mb-l">
|
||||||
Tiny padding + Large margin bottom
|
Tiny padding + Large margin bottom
|
||||||
</div>
|
</div>
|
||||||
<div className="content-neutral-primary bg-neutral-tertiary fw-medium p-l ml-b">
|
<div className="content-neutral-primary bg-pink-500 fw-medium p-l ml-b">
|
||||||
Large padding + Base margin left
|
Large padding + Base margin left
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Canvas, Meta, Source } from '@storybook/addon-docs';
|
import { Canvas, Meta, Source } from "@storybook/addon-docs";
|
||||||
import * as Stories from "./typo.stories";
|
import * as Stories from "./typo.stories";
|
||||||
|
|
||||||
<Meta title="Getting Started/Typography"/>
|
<Meta title="Getting Started/Typography" />
|
||||||
|
|
||||||
# Typo
|
# Typo
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ Cunningham comes with an existing toolkit to deal with typography, and it's easy
|
|||||||
You can use custom utility classes to set the font size of an element. These classes are named using the format `.fs-{size}`.
|
You can use custom utility classes to set the font size of an element. These classes are named using the format `.fs-{size}`.
|
||||||
|
|
||||||
<Source
|
<Source
|
||||||
language='tsx'
|
language="tsx"
|
||||||
dark
|
dark
|
||||||
format={false}
|
format={false}
|
||||||
code={`
|
code={`
|
||||||
@@ -20,12 +20,12 @@ You can use custom utility classes to set the font size of an element. These cla
|
|||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Canvas sourceState="none" of={Stories.Sizes}/>
|
<Canvas sourceState="none" of={Stories.Sizes} />
|
||||||
|
|
||||||
You can customize the values of the font size design tokens with the configuration file this way:
|
You can customize the values of the font size design tokens with the configuration file this way:
|
||||||
|
|
||||||
<Source
|
<Source
|
||||||
language='ts'
|
language="ts"
|
||||||
dark
|
dark
|
||||||
format={true}
|
format={true}
|
||||||
code={`
|
code={`
|
||||||
@@ -53,7 +53,7 @@ You can customize the values of the font size design tokens with the configurati
|
|||||||
You can use custom utility classes to set the font weight of an element. These classes are named using the format `.fw-{weight}`.
|
You can use custom utility classes to set the font weight of an element. These classes are named using the format `.fw-{weight}`.
|
||||||
|
|
||||||
<Source
|
<Source
|
||||||
language='tsx'
|
language="tsx"
|
||||||
dark
|
dark
|
||||||
format={false}
|
format={false}
|
||||||
code={`
|
code={`
|
||||||
@@ -61,12 +61,12 @@ You can use custom utility classes to set the font weight of an element. These c
|
|||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Canvas sourceState="none" of={Stories.Weights}/>
|
<Canvas sourceState="none" of={Stories.Weights} />
|
||||||
|
|
||||||
You can customize the values of the font weight design tokens with the configuration file this way:
|
You can customize the values of the font weight design tokens with the configuration file this way:
|
||||||
|
|
||||||
<Source
|
<Source
|
||||||
language='ts'
|
language="ts"
|
||||||
dark
|
dark
|
||||||
format={true}
|
format={true}
|
||||||
code={`
|
code={`
|
||||||
@@ -98,20 +98,20 @@ The font associated with `f-base` is the default font for the whole application,
|
|||||||
class on every dom element.
|
class on every dom element.
|
||||||
|
|
||||||
<Source
|
<Source
|
||||||
language='tsx'
|
language="tsx"
|
||||||
dark
|
dark
|
||||||
format={false}
|
format={false}
|
||||||
code={`
|
code={`
|
||||||
<div className="clr-gray-800 f-accent">Accent text</div>
|
<div className="clr-content-semantic-neutral-primary f-accent">Accent text</div>
|
||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Canvas sourceState="none" of={Stories.Families}/>
|
<Canvas sourceState="none" of={Stories.Families} />
|
||||||
|
|
||||||
You can customize the values of the font family design tokens with the configuration file this way:
|
You can customize the values of the font family design tokens with the configuration file this way:
|
||||||
|
|
||||||
<Source
|
<Source
|
||||||
language='ts'
|
language="ts"
|
||||||
dark
|
dark
|
||||||
format={true}
|
format={true}
|
||||||
code={`
|
code={`
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ export const Sizes: Story = {
|
|||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
|
||||||
{Object.keys(tokens.themes.default.globals.font.sizes).map((key) => (
|
{Object.keys(tokens.themes.default.globals.font.sizes).map((key) => (
|
||||||
<div key={key} className={"clr-gray-800 fs-" + key}>
|
<div
|
||||||
|
key={key}
|
||||||
|
className={"clr-content-semantic-neutral-primary fs-" + key}
|
||||||
|
>
|
||||||
Using the <code>fs-{key}</code> class
|
Using the <code>fs-{key}</code> class
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -28,7 +31,10 @@ export const Weights: Story = {
|
|||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
|
||||||
{Object.keys(tokens.themes.default.globals.font.weights).map((key) => (
|
{Object.keys(tokens.themes.default.globals.font.weights).map((key) => (
|
||||||
<div key={key} className={"clr-gray-800 fs-l fw-" + key}>
|
<div
|
||||||
|
key={key}
|
||||||
|
className={"clr-content-semantic-neutral-primary fs-l fw-" + key}
|
||||||
|
>
|
||||||
Using the <code>fw-{key}</code> class
|
Using the <code>fw-{key}</code> class
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -42,7 +48,10 @@ export const Families: Story = {
|
|||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
|
||||||
{Object.keys(tokens.themes.default.globals.font.families).map((key) => (
|
{Object.keys(tokens.themes.default.globals.font.families).map((key) => (
|
||||||
<div key={key} className={"clr-gray-800 f-" + key}>
|
<div
|
||||||
|
key={key}
|
||||||
|
className={"clr-content-semantic-neutral-primary f-" + key}
|
||||||
|
>
|
||||||
Using the <code>f-{key}</code> class
|
Using the <code>f-{key}</code> class
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user