@dxos/ui-theme 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +25 -0
- package/package.json +80 -0
- package/src/Tokens.stories.tsx +88 -0
- package/src/config/index.ts +6 -0
- package/src/config/tailwind.ts +250 -0
- package/src/config/tokens/alias-colors.ts +39 -0
- package/src/config/tokens/index.ts +92 -0
- package/src/config/tokens/lengths.ts +97 -0
- package/src/config/tokens/physical-colors.ts +125 -0
- package/src/config/tokens/semantic-colors.ts +27 -0
- package/src/config/tokens/sememes-calls.ts +17 -0
- package/src/config/tokens/sememes-codemirror.ts +50 -0
- package/src/config/tokens/sememes-hue.ts +54 -0
- package/src/config/tokens/sememes-sheet.ts +62 -0
- package/src/config/tokens/sememes-system.ts +302 -0
- package/src/config/tokens/sizes.ts +7 -0
- package/src/config/tokens/types.ts +9 -0
- package/src/docs/theme.drawio.svg +635 -0
- package/src/index.ts +19 -0
- package/src/plugins/esbuild-plugin.ts +65 -0
- package/src/plugins/plugin.ts +130 -0
- package/src/plugins/resolveContent.ts +51 -0
- package/src/styles/components/README.md +6 -0
- package/src/styles/components/anchored-overflow.ts +20 -0
- package/src/styles/components/avatar.ts +96 -0
- package/src/styles/components/breadcrumb.ts +29 -0
- package/src/styles/components/button.ts +48 -0
- package/src/styles/components/dialog.ts +36 -0
- package/src/styles/components/icon-button.ts +20 -0
- package/src/styles/components/icon.ts +19 -0
- package/src/styles/components/index.ts +27 -0
- package/src/styles/components/input.ts +177 -0
- package/src/styles/components/link.ts +26 -0
- package/src/styles/components/list.ts +46 -0
- package/src/styles/components/main.ts +36 -0
- package/src/styles/components/menu.ts +60 -0
- package/src/styles/components/message.ts +36 -0
- package/src/styles/components/popover.ts +40 -0
- package/src/styles/components/scroll-area.ts +43 -0
- package/src/styles/components/select.ts +60 -0
- package/src/styles/components/separator.ts +24 -0
- package/src/styles/components/status.ts +32 -0
- package/src/styles/components/tag.ts +23 -0
- package/src/styles/components/toast.ts +55 -0
- package/src/styles/components/toolbar.ts +29 -0
- package/src/styles/components/tooltip.ts +29 -0
- package/src/styles/components/treegrid.ts +37 -0
- package/src/styles/fragments/density.ts +17 -0
- package/src/styles/fragments/dimension.ts +8 -0
- package/src/styles/fragments/disabled.ts +6 -0
- package/src/styles/fragments/elevation.ts +29 -0
- package/src/styles/fragments/focus.ts +16 -0
- package/src/styles/fragments/group.ts +12 -0
- package/src/styles/fragments/hover.ts +25 -0
- package/src/styles/fragments/index.ts +20 -0
- package/src/styles/fragments/layout.ts +7 -0
- package/src/styles/fragments/motion.ts +6 -0
- package/src/styles/fragments/ornament.ts +10 -0
- package/src/styles/fragments/selected.ts +45 -0
- package/src/styles/fragments/shimmer.ts +9 -0
- package/src/styles/fragments/size.ts +117 -0
- package/src/styles/fragments/surface.ts +29 -0
- package/src/styles/fragments/text.ts +12 -0
- package/src/styles/fragments/valence.ts +46 -0
- package/src/styles/index.ts +7 -0
- package/src/styles/layers/README.md +15 -0
- package/src/styles/layers/anchored-overflow.css +9 -0
- package/src/styles/layers/animation.css +17 -0
- package/src/styles/layers/attention.css +8 -0
- package/src/styles/layers/base.css +25 -0
- package/src/styles/layers/button.css +76 -0
- package/src/styles/layers/can-scroll.css +26 -0
- package/src/styles/layers/checkbox.css +50 -0
- package/src/styles/layers/dialog.css +42 -0
- package/src/styles/layers/drag-preview.css +18 -0
- package/src/styles/layers/focus-ring.css +224 -0
- package/src/styles/layers/hues.css +110 -0
- package/src/styles/layers/index.css +26 -0
- package/src/styles/layers/main.css +160 -0
- package/src/styles/layers/native.css +20 -0
- package/src/styles/layers/positioning.css +23 -0
- package/src/styles/layers/size.css +397 -0
- package/src/styles/layers/surfaces.css +31 -0
- package/src/styles/layers/tag.css +132 -0
- package/src/styles/layers/tldraw.css +91 -0
- package/src/styles/layers/tokens.css +45 -0
- package/src/styles/layers/typography.css +157 -0
- package/src/styles/theme.ts +69 -0
- package/src/tailwind.ts +5 -0
- package/src/theme.css +9 -0
- package/src/types.ts +7 -0
- package/src/typings.d.ts +8 -0
- package/src/util/hash-styles.ts +168 -0
- package/src/util/index.ts +6 -0
- package/src/util/mx.ts +51 -0
- package/src/util/withLogical.ts +114 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { AccompanyingSeries, ColorsPhysicalLayer, Gamut, HelicalArcSeries, PhysicalSeries } from '@ch-ui/tokens';
|
|
6
|
+
|
|
7
|
+
import { type PhysicalPalette } from './types';
|
|
8
|
+
|
|
9
|
+
const reflectiveRelation = {
|
|
10
|
+
initial: 1_000,
|
|
11
|
+
slope: -1_000,
|
|
12
|
+
method: 'floor',
|
|
13
|
+
} satisfies AccompanyingSeries;
|
|
14
|
+
|
|
15
|
+
const gamuts: (Gamut & string)[] = ['srgb', 'p3', 'rec2020'];
|
|
16
|
+
|
|
17
|
+
const DEG_RAD = Math.PI / 180;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a color palette configuration for a given hue value.
|
|
21
|
+
*
|
|
22
|
+
* @param hue - A number from 0-16 representing different hue angles
|
|
23
|
+
* @returns A PhysicalPalette configuration with:
|
|
24
|
+
* - keyPoint: [lightness, chroma, hue] in LCH color space
|
|
25
|
+
* - lightness: Fixed at 0.5 (50%)
|
|
26
|
+
* - chroma: Varies sinusoidally around 0.13 based on hue angle
|
|
27
|
+
* - hue: Calculated by mapping input 0-16 to 26-386 degrees
|
|
28
|
+
* - Control points and torsion for color interpolation
|
|
29
|
+
*/
|
|
30
|
+
const hueKeyPoint = (hue: number): PhysicalPalette => {
|
|
31
|
+
const hueDeg = (360 * (hue / 17) + 26) % 360;
|
|
32
|
+
return {
|
|
33
|
+
keyPoint: [0.5, 0.13 + 0.024 * Math.sin((hueDeg - 15) * DEG_RAD), hueDeg],
|
|
34
|
+
lowerCp: 1,
|
|
35
|
+
upperCp: 1,
|
|
36
|
+
torsion: 0,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const huePalettes = {
|
|
41
|
+
red: hueKeyPoint(0),
|
|
42
|
+
orange: hueKeyPoint(1),
|
|
43
|
+
amber: hueKeyPoint(2),
|
|
44
|
+
yellow: hueKeyPoint(3),
|
|
45
|
+
lime: hueKeyPoint(4),
|
|
46
|
+
green: hueKeyPoint(5),
|
|
47
|
+
emerald: hueKeyPoint(6),
|
|
48
|
+
teal: hueKeyPoint(7),
|
|
49
|
+
cyan: hueKeyPoint(8),
|
|
50
|
+
sky: hueKeyPoint(9),
|
|
51
|
+
blue: hueKeyPoint(10),
|
|
52
|
+
indigo: hueKeyPoint(11),
|
|
53
|
+
violet: hueKeyPoint(12),
|
|
54
|
+
purple: hueKeyPoint(13),
|
|
55
|
+
fuchsia: hueKeyPoint(14),
|
|
56
|
+
pink: hueKeyPoint(15),
|
|
57
|
+
rose: hueKeyPoint(16),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The keyPoint represents the LCH value:
|
|
62
|
+
* - Lightness: 0-1, should usually set the keyPoint at or near 0.5
|
|
63
|
+
* - Chroma: min 0, max 0.08–0.5 depending on hue and gamut, theme will clamp final value to within gamut’s range
|
|
64
|
+
* - Hue: 0-360 (~26 “red”, ~141 “green”, ~262 “blue”)
|
|
65
|
+
*
|
|
66
|
+
* NOTE: Rebuild the theme and restart the dev server to see changes.
|
|
67
|
+
*
|
|
68
|
+
* Theme references:
|
|
69
|
+
* https://oklch.com
|
|
70
|
+
* https://colorsublime.github.io
|
|
71
|
+
* https://github.com/microsoft/vscode-docs/blob/main/api/extension-guides/color-theme.md#create-a-new-color-theme
|
|
72
|
+
* https://raw.githubusercontent.com/microsoft/vscode/main/src/vs/workbench/services/themes/common/colorThemeSchema.ts
|
|
73
|
+
* https://tailwindcss.com/docs/colors
|
|
74
|
+
*/
|
|
75
|
+
const systemPalettes = {
|
|
76
|
+
neutral: {
|
|
77
|
+
keyPoint: [0.5, 0.001, 260],
|
|
78
|
+
lowerCp: 0,
|
|
79
|
+
upperCp: 0,
|
|
80
|
+
torsion: 0,
|
|
81
|
+
// Values used directly.
|
|
82
|
+
// TODO(burdon): Audit.
|
|
83
|
+
values: [25, 50, 75, 100, 150, 200, 250, 300, 400, 500, 600, 700, 750, 800, 850, 900],
|
|
84
|
+
} satisfies PhysicalPalette,
|
|
85
|
+
|
|
86
|
+
// https://oklch.com/#0.5,0.2,260,100 (#0559d2)
|
|
87
|
+
primary: {
|
|
88
|
+
keyPoint: [0.5, 0.2, 260],
|
|
89
|
+
lowerCp: 0.86,
|
|
90
|
+
upperCp: 1,
|
|
91
|
+
torsion: -30,
|
|
92
|
+
// Values used directly.
|
|
93
|
+
// TODO(burdon): Audit.
|
|
94
|
+
values: [100, 150, 200, 350, 400, 450, 500, 750, 800, 850],
|
|
95
|
+
} satisfies PhysicalPalette,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const physicalSeries = {
|
|
99
|
+
...huePalettes,
|
|
100
|
+
...systemPalettes,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const physicalColors: ColorsPhysicalLayer = {
|
|
104
|
+
namespace: 'dx-',
|
|
105
|
+
definitions: {
|
|
106
|
+
// @ts-ignore
|
|
107
|
+
series: physicalSeries,
|
|
108
|
+
accompanyingSeries: { reflectiveRelation },
|
|
109
|
+
},
|
|
110
|
+
conditions: {
|
|
111
|
+
srgb: [':root, .dark'],
|
|
112
|
+
p3: ['@media (color-gamut: p3)', ':root, .dark'],
|
|
113
|
+
rec2020: ['@media (color-gamut: rec2020)', ':root, .dark'],
|
|
114
|
+
},
|
|
115
|
+
series: Object.entries(physicalSeries).reduce((acc: ColorsPhysicalLayer['series'], [id]) => {
|
|
116
|
+
acc[id] = gamuts.reduce((acc: PhysicalSeries<Gamut & string, HelicalArcSeries>, gamut) => {
|
|
117
|
+
acc[gamut] = {
|
|
118
|
+
extends: id,
|
|
119
|
+
physicalValueRelation: { extends: 'reflectiveRelation' },
|
|
120
|
+
};
|
|
121
|
+
return acc;
|
|
122
|
+
}, {});
|
|
123
|
+
return acc;
|
|
124
|
+
}, {}),
|
|
125
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { HelicalArcValue, SemanticLayer } from '@ch-ui/tokens';
|
|
6
|
+
|
|
7
|
+
import { callsSememes } from './sememes-calls';
|
|
8
|
+
import { codemirrorSememes } from './sememes-codemirror';
|
|
9
|
+
import { hueSememes } from './sememes-hue';
|
|
10
|
+
import { sheetSememes } from './sememes-sheet';
|
|
11
|
+
import { systemSememes } from './sememes-system';
|
|
12
|
+
|
|
13
|
+
export const semanticColors = {
|
|
14
|
+
conditions: {
|
|
15
|
+
light: [':root'],
|
|
16
|
+
dark: ['.dark'],
|
|
17
|
+
},
|
|
18
|
+
sememes: {
|
|
19
|
+
// Define each set of sememes in its own file.
|
|
20
|
+
...callsSememes,
|
|
21
|
+
...codemirrorSememes,
|
|
22
|
+
...hueSememes,
|
|
23
|
+
...sheetSememes,
|
|
24
|
+
...systemSememes,
|
|
25
|
+
},
|
|
26
|
+
namespace: 'dx-',
|
|
27
|
+
} satisfies SemanticLayer<string, string, HelicalArcValue>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type ColorSememes } from './types';
|
|
6
|
+
|
|
7
|
+
// TODO(burdon): This should be broader than just calls.
|
|
8
|
+
export const callsSememes: ColorSememes = {
|
|
9
|
+
callActive: {
|
|
10
|
+
light: ['green', '500'],
|
|
11
|
+
dark: ['green', '500'],
|
|
12
|
+
},
|
|
13
|
+
callAlert: {
|
|
14
|
+
light: ['rose', '500'],
|
|
15
|
+
dark: ['rose', '500'],
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type ColorSememes } from './types';
|
|
6
|
+
|
|
7
|
+
export const codemirrorSememes = {
|
|
8
|
+
// NOTE: background styles for the main content area must have transparency otherwise they will mask the selection.
|
|
9
|
+
cmCodeblock: {
|
|
10
|
+
light: ['neutral', '500/.1'],
|
|
11
|
+
dark: ['neutral', '500/.1'],
|
|
12
|
+
},
|
|
13
|
+
cmActiveLine: {
|
|
14
|
+
light: ['neutral', '200/.5'],
|
|
15
|
+
dark: ['neutral', '800/.5'],
|
|
16
|
+
},
|
|
17
|
+
cmSeparator: {
|
|
18
|
+
light: ['primary', 500],
|
|
19
|
+
dark: ['primary', 500],
|
|
20
|
+
},
|
|
21
|
+
cmCursor: {
|
|
22
|
+
light: ['neutral', 900],
|
|
23
|
+
dark: ['neutral', 100],
|
|
24
|
+
},
|
|
25
|
+
cmSelection: {
|
|
26
|
+
light: ['primary', '400/.5'],
|
|
27
|
+
dark: ['primary', '600/.5'],
|
|
28
|
+
},
|
|
29
|
+
cmFocusedSelection: {
|
|
30
|
+
light: ['primary', 400],
|
|
31
|
+
dark: ['primary', 600],
|
|
32
|
+
},
|
|
33
|
+
cmHighlight: {
|
|
34
|
+
light: ['neutral', 950],
|
|
35
|
+
dark: ['neutral', 50],
|
|
36
|
+
},
|
|
37
|
+
cmHighlightSurface: {
|
|
38
|
+
light: ['sky', 200],
|
|
39
|
+
dark: ['cyan', 600],
|
|
40
|
+
},
|
|
41
|
+
// TODO(burdon): Factor out defs in common with sheet.
|
|
42
|
+
cmCommentText: {
|
|
43
|
+
light: ['neutral', 50],
|
|
44
|
+
dark: ['neutral', 950],
|
|
45
|
+
},
|
|
46
|
+
cmCommentSurface: {
|
|
47
|
+
light: ['amber', 700],
|
|
48
|
+
dark: ['amber', 200],
|
|
49
|
+
},
|
|
50
|
+
} satisfies ColorSememes;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { huePalettes } from './physical-colors';
|
|
6
|
+
import { type ColorAliases, type ColorSememes } from './types';
|
|
7
|
+
|
|
8
|
+
export const hueSememes: ColorSememes = [...Object.keys(huePalettes), 'neutral', 'primary'].reduce(
|
|
9
|
+
(acc: ColorSememes, palette) => {
|
|
10
|
+
acc[`${palette}Cursor`] = {
|
|
11
|
+
light: [palette, 400],
|
|
12
|
+
dark: [palette, 300],
|
|
13
|
+
};
|
|
14
|
+
acc[`${palette}Text`] = {
|
|
15
|
+
light: [palette, 550],
|
|
16
|
+
dark: [palette, 300],
|
|
17
|
+
};
|
|
18
|
+
acc[`${palette}Fill`] = {
|
|
19
|
+
light: [palette, 500],
|
|
20
|
+
dark: [palette, 500],
|
|
21
|
+
};
|
|
22
|
+
acc[`${palette}Surface`] = {
|
|
23
|
+
light: [palette, 200],
|
|
24
|
+
dark: [palette, 700],
|
|
25
|
+
};
|
|
26
|
+
acc[`${palette}SurfaceText`] = {
|
|
27
|
+
light: [palette, 700],
|
|
28
|
+
dark: [palette, 200],
|
|
29
|
+
};
|
|
30
|
+
acc[`${palette}Screen`] = {
|
|
31
|
+
light: [palette, 100],
|
|
32
|
+
dark: [palette, 800],
|
|
33
|
+
};
|
|
34
|
+
return acc;
|
|
35
|
+
},
|
|
36
|
+
{},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const valenceAliasSememeStems = ['Text', 'Surface', 'SurfaceText', 'Fill', 'Cursor'];
|
|
40
|
+
const valenceMapping = {
|
|
41
|
+
emerald: ['success'],
|
|
42
|
+
cyan: ['info'],
|
|
43
|
+
amber: ['warning'],
|
|
44
|
+
rose: ['error'],
|
|
45
|
+
primary: ['current'],
|
|
46
|
+
fuchsia: ['internal'],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const valenceAliases: ColorAliases = valenceAliasSememeStems.reduce((acc: ColorAliases, stem) => {
|
|
50
|
+
return Object.entries(valenceMapping).reduce((acc: ColorAliases, [hue, valences]) => {
|
|
51
|
+
acc[`${hue}${stem}`] = { root: valences.map((valence) => `${valence}${stem}`) };
|
|
52
|
+
return acc;
|
|
53
|
+
}, acc);
|
|
54
|
+
}, {});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { ColorAliases, ColorSememes } from './types';
|
|
6
|
+
|
|
7
|
+
export const sheetSememes = {
|
|
8
|
+
// NOTE: background styles for the main content area must have transparency otherwise they will mask the selection.
|
|
9
|
+
axisSurface: {
|
|
10
|
+
light: ['neutral', 50],
|
|
11
|
+
dark: ['neutral', 800],
|
|
12
|
+
},
|
|
13
|
+
axisText: {
|
|
14
|
+
light: ['neutral', 800],
|
|
15
|
+
dark: ['neutral', 200],
|
|
16
|
+
},
|
|
17
|
+
axisSelectedSurface: {
|
|
18
|
+
light: ['neutral', 100],
|
|
19
|
+
dark: ['neutral', 900],
|
|
20
|
+
},
|
|
21
|
+
axisSelectedText: {
|
|
22
|
+
light: ['neutral', 100],
|
|
23
|
+
dark: ['neutral', 900],
|
|
24
|
+
},
|
|
25
|
+
gridCell: {
|
|
26
|
+
// TODO(thure): Why override only dark?
|
|
27
|
+
light: ['neutral', '50/0'],
|
|
28
|
+
dark: ['neutral', 850],
|
|
29
|
+
},
|
|
30
|
+
gridCellSelected: {
|
|
31
|
+
// TODO(thure): Can this not just use `attention`?
|
|
32
|
+
light: ['neutral', 50],
|
|
33
|
+
dark: ['neutral', 800],
|
|
34
|
+
},
|
|
35
|
+
gridOverlay: {
|
|
36
|
+
light: ['primary', '500/.5'],
|
|
37
|
+
dark: ['primary', '500/.5'],
|
|
38
|
+
},
|
|
39
|
+
gridSelectionOverlay: {
|
|
40
|
+
light: ['primary', '500/.2'],
|
|
41
|
+
dark: ['primary', '500/.2'],
|
|
42
|
+
},
|
|
43
|
+
gridHighlight: {
|
|
44
|
+
light: ['emerald', '500/.5'],
|
|
45
|
+
dark: ['emerald', '500/.5'],
|
|
46
|
+
},
|
|
47
|
+
// TODO(burdon): Factor out def (in common with editor).
|
|
48
|
+
gridCommented: {
|
|
49
|
+
light: ['green', 200],
|
|
50
|
+
dark: ['green', 600],
|
|
51
|
+
},
|
|
52
|
+
gridCommentedActive: {
|
|
53
|
+
light: ['green', '200/.5'],
|
|
54
|
+
dark: ['green', '600/.5'],
|
|
55
|
+
},
|
|
56
|
+
} satisfies ColorSememes;
|
|
57
|
+
|
|
58
|
+
export const sheetAliases = {
|
|
59
|
+
activeSurface: { root: ['gridLine'] },
|
|
60
|
+
accentFocusIndicator: { root: ['gridFocusIndicatorColor'] },
|
|
61
|
+
neutralFocusIndicator: { gridFocusStack: ['gridFocusIndicatorColor'] },
|
|
62
|
+
} satisfies ColorAliases;
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// TODO(thure): TS2742
|
|
6
|
+
/* eslint-disable unused-imports/no-unused-imports */
|
|
7
|
+
import * as _colors from '@ch-ui/colors';
|
|
8
|
+
|
|
9
|
+
import { type ColorAliases, type ColorSememes } from './types';
|
|
10
|
+
|
|
11
|
+
// TODO(burdon): Move to util.
|
|
12
|
+
const getMapValue = <T>(map: Record<string, T>, key: string, defaultValue: () => T): T => {
|
|
13
|
+
let value = map[key];
|
|
14
|
+
if (!value) {
|
|
15
|
+
value = defaultValue();
|
|
16
|
+
map[key] = value;
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type Sememe = ColorSememes[string];
|
|
22
|
+
|
|
23
|
+
const applyAlpha = (sememe: Sememe, alpha: number): Sememe => {
|
|
24
|
+
if (alpha >= 1) {
|
|
25
|
+
return sememe;
|
|
26
|
+
} else {
|
|
27
|
+
return {
|
|
28
|
+
light: [sememe.light![0], `${sememe.light![1]}/${alpha}`],
|
|
29
|
+
dark: [sememe.dark![0], `${sememe.dark![1]}/${alpha}`],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Luminosity extrema and key points.
|
|
35
|
+
|
|
36
|
+
// Both elevation cadences go from darker to lighter from “elevation” 0 to `ELEVATION_SCALE`,
|
|
37
|
+
// whereas both contrast cadences go from highest contrast at 0 to lowest contrast at `CONTRAST_SCALE`.
|
|
38
|
+
|
|
39
|
+
const DARK_ELEVATION_MIN = 855;
|
|
40
|
+
const DARK_ELEVATION_MAX = 731;
|
|
41
|
+
|
|
42
|
+
const DARK_CONTRAST_MIN = 750;
|
|
43
|
+
const DARK_CONTRAST_MAX = 665;
|
|
44
|
+
|
|
45
|
+
const LIGHT_ELEVATION_MIN = 0;
|
|
46
|
+
const LIGHT_ELEVATION_MAX = 0;
|
|
47
|
+
|
|
48
|
+
const LIGHT_CONTRAST_MIN = 82;
|
|
49
|
+
const LIGHT_CONTRAST_MAX = 24;
|
|
50
|
+
|
|
51
|
+
const ELEVATION_SCALE = 2;
|
|
52
|
+
const CONTRAST_SCALE = 3;
|
|
53
|
+
|
|
54
|
+
const darkElevationCadence = (depth: number) =>
|
|
55
|
+
Math.round(
|
|
56
|
+
DARK_ELEVATION_MAX + (DARK_ELEVATION_MIN - DARK_ELEVATION_MAX) * ((ELEVATION_SCALE - depth) / ELEVATION_SCALE),
|
|
57
|
+
);
|
|
58
|
+
const darkContrastCadence = (depth: number) =>
|
|
59
|
+
Math.round(
|
|
60
|
+
DARK_CONTRAST_MAX + (DARK_CONTRAST_MIN - DARK_CONTRAST_MAX) * ((ELEVATION_SCALE - depth) / ELEVATION_SCALE),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const lightElevationCadence = (depth: number) =>
|
|
64
|
+
Math.round(
|
|
65
|
+
LIGHT_ELEVATION_MIN + (LIGHT_ELEVATION_MAX - LIGHT_ELEVATION_MIN) * ((CONTRAST_SCALE - depth) / CONTRAST_SCALE),
|
|
66
|
+
);
|
|
67
|
+
const lightContrastCadence = (depth: number) =>
|
|
68
|
+
Math.round(LIGHT_CONTRAST_MAX + (LIGHT_CONTRAST_MIN - LIGHT_CONTRAST_MAX) * (depth / CONTRAST_SCALE));
|
|
69
|
+
|
|
70
|
+
const elevationCadence = (lightDepth: number, darkDepth: number = lightDepth, alpha: number = 1): Sememe =>
|
|
71
|
+
applyAlpha(
|
|
72
|
+
{
|
|
73
|
+
light: ['neutral', lightElevationCadence(lightDepth)],
|
|
74
|
+
dark: ['neutral', darkElevationCadence(darkDepth)],
|
|
75
|
+
},
|
|
76
|
+
alpha,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const contrastCadence = (lightDepth: number, darkDepth: number = lightDepth, alpha: number = 1): Sememe =>
|
|
80
|
+
applyAlpha(
|
|
81
|
+
{
|
|
82
|
+
light: ['neutral', lightContrastCadence(lightDepth)],
|
|
83
|
+
dark: ['neutral', darkContrastCadence(darkDepth)],
|
|
84
|
+
},
|
|
85
|
+
alpha,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
export const systemSememes = {
|
|
89
|
+
//
|
|
90
|
+
// Elevation cadence tokens
|
|
91
|
+
//
|
|
92
|
+
|
|
93
|
+
baseSurface: elevationCadence(0),
|
|
94
|
+
groupSurface: elevationCadence(1),
|
|
95
|
+
modalSurface: elevationCadence(2, 1.7),
|
|
96
|
+
|
|
97
|
+
//
|
|
98
|
+
// Contrast cadence tokens
|
|
99
|
+
//
|
|
100
|
+
|
|
101
|
+
textInputSurfaceBase: contrastCadence(0, 0),
|
|
102
|
+
textInputSurfaceGroup: contrastCadence(0, 0.5),
|
|
103
|
+
textInputSurfaceModal: contrastCadence(0, 1),
|
|
104
|
+
|
|
105
|
+
inputSurfaceBase: contrastCadence(0.8, 0.33),
|
|
106
|
+
inputSurfaceGroup: contrastCadence(0.8, 0.66),
|
|
107
|
+
inputSurfaceModal: contrastCadence(0.8, 1),
|
|
108
|
+
|
|
109
|
+
hoverSurfaceBase: contrastCadence(2, 1.5),
|
|
110
|
+
hoverSurfaceGroup: contrastCadence(2, 2),
|
|
111
|
+
hoverSurfaceModal: contrastCadence(2, 2.5),
|
|
112
|
+
|
|
113
|
+
separatorBase: contrastCadence(3, 2),
|
|
114
|
+
separatorGroup: contrastCadence(3, 2.5),
|
|
115
|
+
separatorModal: contrastCadence(3, 3),
|
|
116
|
+
|
|
117
|
+
subduedSeparator: contrastCadence(3, 1),
|
|
118
|
+
|
|
119
|
+
unAccent: {
|
|
120
|
+
light: ['neutral', 400],
|
|
121
|
+
dark: ['neutral', 400],
|
|
122
|
+
},
|
|
123
|
+
unAccentHover: {
|
|
124
|
+
light: ['neutral', 450],
|
|
125
|
+
dark: ['neutral', 450],
|
|
126
|
+
},
|
|
127
|
+
hoverOverlay: {
|
|
128
|
+
light: ['neutral', '450/.1'],
|
|
129
|
+
dark: ['neutral', '450/.1'],
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
//
|
|
133
|
+
// Special surfaces.
|
|
134
|
+
//
|
|
135
|
+
|
|
136
|
+
// Screen overlay for modal dialogs.
|
|
137
|
+
scrimSurface: applyAlpha(
|
|
138
|
+
{
|
|
139
|
+
light: ['neutral', LIGHT_CONTRAST_MAX],
|
|
140
|
+
dark: ['neutral', DARK_ELEVATION_MIN],
|
|
141
|
+
},
|
|
142
|
+
0.65,
|
|
143
|
+
),
|
|
144
|
+
|
|
145
|
+
// High contrast for focused interactive elements. (Technically this is part of the surface cadence, but the contrast cadence is on the opposite side of the elevation cadence as this point.)
|
|
146
|
+
focusSurface: {
|
|
147
|
+
light: ['neutral', 0],
|
|
148
|
+
dark: ['neutral', 1000],
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
deckSurface: {
|
|
152
|
+
light: ['neutral', 50],
|
|
153
|
+
dark: ['neutral', 950],
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
// For tooltips only; the highest elevation from the opposite theme
|
|
157
|
+
inverseSurface: {
|
|
158
|
+
light: ['neutral', DARK_ELEVATION_MIN],
|
|
159
|
+
dark: ['neutral', LIGHT_ELEVATION_MIN],
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
//
|
|
163
|
+
// Accent surfaces
|
|
164
|
+
//
|
|
165
|
+
|
|
166
|
+
accentSurfaceRelated: {
|
|
167
|
+
light: ['primary', '300/.1'],
|
|
168
|
+
dark: ['primary', '400/.1'],
|
|
169
|
+
},
|
|
170
|
+
accentSurfaceHover: {
|
|
171
|
+
light: ['primary', 600],
|
|
172
|
+
dark: ['primary', 475],
|
|
173
|
+
},
|
|
174
|
+
accentSurface: {
|
|
175
|
+
light: ['primary', 500],
|
|
176
|
+
dark: ['primary', 500],
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
//
|
|
180
|
+
// Text (text-) and other foregrounds
|
|
181
|
+
// TODO(thure): Establish contrast-order cadence for text.
|
|
182
|
+
//
|
|
183
|
+
|
|
184
|
+
baseText: {
|
|
185
|
+
light: ['neutral', 1000],
|
|
186
|
+
dark: ['neutral', 50],
|
|
187
|
+
},
|
|
188
|
+
inverseSurfaceText: {
|
|
189
|
+
light: ['neutral', 50],
|
|
190
|
+
dark: ['neutral', 1000],
|
|
191
|
+
},
|
|
192
|
+
description: {
|
|
193
|
+
light: ['neutral', 550],
|
|
194
|
+
dark: ['neutral', 350],
|
|
195
|
+
},
|
|
196
|
+
subdued: {
|
|
197
|
+
light: ['neutral', 700],
|
|
198
|
+
dark: ['neutral', 300],
|
|
199
|
+
},
|
|
200
|
+
placeholder: {
|
|
201
|
+
light: ['neutral', 500],
|
|
202
|
+
dark: ['neutral', 500],
|
|
203
|
+
},
|
|
204
|
+
accentText: {
|
|
205
|
+
light: ['primary', 550],
|
|
206
|
+
dark: ['primary', 400],
|
|
207
|
+
},
|
|
208
|
+
accentSurfaceText: {
|
|
209
|
+
light: ['neutral', 0],
|
|
210
|
+
dark: ['neutral', 0],
|
|
211
|
+
},
|
|
212
|
+
accentTextHover: {
|
|
213
|
+
light: ['primary', 500],
|
|
214
|
+
dark: ['primary', 350],
|
|
215
|
+
},
|
|
216
|
+
accentFocusIndicator: {
|
|
217
|
+
light: ['primary', 300],
|
|
218
|
+
dark: ['primary', 450],
|
|
219
|
+
},
|
|
220
|
+
neutralFocusIndicator: {
|
|
221
|
+
light: ['neutral', 300],
|
|
222
|
+
dark: ['neutral', 550],
|
|
223
|
+
},
|
|
224
|
+
} satisfies ColorSememes;
|
|
225
|
+
|
|
226
|
+
type SememeName = keyof typeof systemSememes;
|
|
227
|
+
type SememeKey = 'root' | 'group' | 'modal';
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Alias map.
|
|
231
|
+
*/
|
|
232
|
+
const aliasDefs: Record<string, Partial<Record<SememeKey, SememeName>>> = {
|
|
233
|
+
// Selected items, current items, other surfaces needing special contrast against baseSurface.
|
|
234
|
+
activeSurface: {
|
|
235
|
+
root: 'inputSurfaceBase',
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
// Main sidebar panel.
|
|
239
|
+
sidebarSurface: {
|
|
240
|
+
root: 'groupSurface',
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
// Plank header.
|
|
244
|
+
headerSurface: {
|
|
245
|
+
root: 'groupSurface',
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
// Toolbars, table/sheet headers, etc.
|
|
249
|
+
toolbarSurface: {
|
|
250
|
+
root: 'groupSurface',
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
// Forms, cards, etc.
|
|
254
|
+
cardSurface: {
|
|
255
|
+
root: 'groupSurface',
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
// Secondary aliases.
|
|
259
|
+
textInputSurface: {
|
|
260
|
+
root: 'textInputSurfaceBase',
|
|
261
|
+
group: 'textInputSurfaceGroup',
|
|
262
|
+
modal: 'textInputSurfaceModal',
|
|
263
|
+
},
|
|
264
|
+
inputSurface: {
|
|
265
|
+
root: 'inputSurfaceBase',
|
|
266
|
+
group: 'inputSurfaceGroup',
|
|
267
|
+
modal: 'inputSurfaceModal',
|
|
268
|
+
},
|
|
269
|
+
hoverSurface: {
|
|
270
|
+
root: 'hoverSurfaceBase',
|
|
271
|
+
group: 'hoverSurfaceGroup',
|
|
272
|
+
modal: 'hoverSurfaceModal',
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
// TODO(thure): Rename uses of this token to `focusSurface` and remove this alias.
|
|
276
|
+
attention: {
|
|
277
|
+
root: 'focusSurface',
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// In “master-detail” patterns, the background of the item in the list which is enumerated in the adjacent view.
|
|
281
|
+
// TODO(burdon): Review tokens.
|
|
282
|
+
currentRelated: {
|
|
283
|
+
root: 'modalSurface',
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
// Borders and dividers.
|
|
287
|
+
separator: {
|
|
288
|
+
root: 'separatorBase',
|
|
289
|
+
group: 'separatorGroup',
|
|
290
|
+
modal: 'separatorModal',
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export const systemAliases: ColorAliases = Object.entries(aliasDefs).reduce((aliases, [alias, values]) => {
|
|
295
|
+
Object.entries(values).forEach(([key, sememe]) => {
|
|
296
|
+
const record = getMapValue(aliases, sememe, () => ({}));
|
|
297
|
+
const list = getMapValue<string[]>(record, key, () => []);
|
|
298
|
+
list.push(alias);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
return aliases;
|
|
302
|
+
}, {} as ColorAliases);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { AliasLayer, HelicalArcValue, ResolvedHelicalArcSeries, SemanticLayer } from '@ch-ui/tokens';
|
|
6
|
+
|
|
7
|
+
export type PhysicalPalette = Omit<ResolvedHelicalArcSeries, 'extends' | 'physicalValueRelation'>;
|
|
8
|
+
export type ColorSememes = SemanticLayer<string, string, HelicalArcValue>['sememes'];
|
|
9
|
+
export type ColorAliases = AliasLayer<string>['aliases'];
|