@ids-group-ltd/ids-design-system 0.2.2 → 0.4.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/README.md +2 -2
- package/fesm2022/ids-group-ltd-ids-design-system.mjs +3208 -531
- package/fesm2022/ids-group-ltd-ids-design-system.mjs.map +1 -1
- package/package.json +4 -3
- package/schematics/collection.json +10 -0
- package/schematics/ng-add/index.js +47 -0
- package/schematics/ng-add/schema.json +13 -0
- package/styles/_breakpoints.scss +50 -0
- package/styles/_dropdown-overlay.scss +4 -3
- package/styles/_ds-color.scss +36 -0
- package/styles/_fonts.scss +11 -5
- package/styles/_layout-utils.scss +2 -2
- package/styles/_link.scss +3 -2
- package/styles/_overlay-motion.scss +18 -0
- package/styles/_page-grid.scss +6 -3
- package/styles/_reset.scss +6 -1
- package/styles/_scrollbar.scss +2 -1
- package/styles/_theme-activation.scss +49 -0
- package/styles/_tokens-charts.scss +18 -18
- package/styles/_tokens.scss +74 -25
- package/styles/_typography.scss +21 -21
- package/styles/ds.scss +15 -9
- package/themes/README.md +72 -58
- package/themes/{default/_palette.scss → _base-palette.scss} +67 -50
- package/themes/{default/_theme.scss → _semantic.scss} +103 -87
- package/themes/dark/_palette.scss +39 -0
- package/themes/dark/_theme.scss +122 -0
- package/themes/light/_palette.scss +34 -0
- package/themes/light/_theme.scss +15 -0
- package/types/ids-group-ltd-ids-design-system.d.ts +738 -36
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
// =========================================================================
|
|
2
|
-
//
|
|
2
|
+
// Base palette — Tier 1 theme-independent primitives.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// channels (--blue-h/s/l, --red-h/s/l,
|
|
7
|
-
//
|
|
8
|
-
// colour is "for". The --neutral-* FAMILY SLOT (which gray ramp is active)
|
|
9
|
-
// is semantic, so it lives in the theme (_theme.scss), not here.
|
|
4
|
+
// Emits at :root. Colour ramps and channels that are identical in every
|
|
5
|
+
// theme: hue ramps (--cool-gray-*, --brand-gray-*, --blue-*, --red-*, …),
|
|
6
|
+
// HSL channels (--blue-h/s/l, --red-h/s/l), shadow substrate h/s (NOT -l —
|
|
7
|
+
// that is per-theme), static anchors, and font families.
|
|
10
8
|
//
|
|
11
|
-
// The
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// A sibling palette (e.g. _palette-dark.scss) ships different ramp values; pair
|
|
16
|
-
// it with a matching theme file. Default = "Blue + Slate baseline".
|
|
9
|
+
// The --neutral-* FAMILY SLOT (which gray ramp is active) is per-theme and
|
|
10
|
+
// lives in each theme's _palette.scss mixin. Components consume only semantic
|
|
11
|
+
// roles; they never reference this file directly.
|
|
17
12
|
// =========================================================================
|
|
18
13
|
|
|
19
14
|
:root {
|
|
@@ -22,7 +17,7 @@
|
|
|
22
17
|
// 210 → 227 toward the brand hue (229) as it darkens; chroma follows a
|
|
23
18
|
// designed U-curve (S 40% → 12% → 37%). Appearance name, Carbon-style.
|
|
24
19
|
// -----------------------------------------------------------------------
|
|
25
|
-
--cool-gray-0: #
|
|
20
|
+
--cool-gray-0: #FFF;
|
|
26
21
|
--cool-gray-50: #F8FAFC;
|
|
27
22
|
--cool-gray-100: #F1F4F9;
|
|
28
23
|
--cool-gray-150: #E8ECF2;
|
|
@@ -44,19 +39,19 @@
|
|
|
44
39
|
// cool-gray to within rounding.
|
|
45
40
|
// -----------------------------------------------------------------------
|
|
46
41
|
--neutral-h: var(--primary-h, var(--blue-h));
|
|
47
|
-
--brand-gray-0: #
|
|
48
|
-
--brand-gray-50: hsl(calc(var(--neutral-h) - 19)
|
|
49
|
-
--brand-gray-100: hsl(calc(var(--neutral-h) - 12)
|
|
50
|
-
--brand-gray-150: hsl(calc(var(--neutral-h) - 13)
|
|
51
|
-
--brand-gray-200: hsl(calc(var(--neutral-h) - 10)
|
|
52
|
-
--brand-gray-300: hsl(calc(var(--neutral-h) - 10)
|
|
53
|
-
--brand-gray-400: hsl(calc(var(--neutral-h) - 8)
|
|
54
|
-
--brand-gray-500: hsl(calc(var(--neutral-h) - 6)
|
|
55
|
-
--brand-gray-600: hsl(calc(var(--neutral-h) - 5)
|
|
56
|
-
--brand-gray-700: hsl(calc(var(--neutral-h) - 4)
|
|
57
|
-
--brand-gray-800: hsl(calc(var(--neutral-h) - 3)
|
|
58
|
-
--brand-gray-900: hsl(calc(var(--neutral-h) - 2)
|
|
59
|
-
--brand-gray-950: hsl(calc(var(--neutral-h) - 3)
|
|
42
|
+
--brand-gray-0: #FFF;
|
|
43
|
+
--brand-gray-50: hsl(calc(var(--neutral-h) - 19) 40.0% 98.0%);
|
|
44
|
+
--brand-gray-100: hsl(calc(var(--neutral-h) - 12) 40.0% 96.1%);
|
|
45
|
+
--brand-gray-150: hsl(calc(var(--neutral-h) - 13) 27.8% 92.9%);
|
|
46
|
+
--brand-gray-200: hsl(calc(var(--neutral-h) - 10) 25.0% 89.0%);
|
|
47
|
+
--brand-gray-300: hsl(calc(var(--neutral-h) - 10) 19.6% 80.0%);
|
|
48
|
+
--brand-gray-400: hsl(calc(var(--neutral-h) - 8) 14.3% 65.7%);
|
|
49
|
+
--brand-gray-500: hsl(calc(var(--neutral-h) - 6) 11.9% 47.6%);
|
|
50
|
+
--brand-gray-600: hsl(calc(var(--neutral-h) - 5) 14.9% 35.5%);
|
|
51
|
+
--brand-gray-700: hsl(calc(var(--neutral-h) - 4) 18.5% 25.5%);
|
|
52
|
+
--brand-gray-800: hsl(calc(var(--neutral-h) - 3) 23.1% 17.8%);
|
|
53
|
+
--brand-gray-900: hsl(calc(var(--neutral-h) - 2) 29.0% 12.2%);
|
|
54
|
+
--brand-gray-950: hsl(calc(var(--neutral-h) - 3) 37.1% 6.9%);
|
|
60
55
|
|
|
61
56
|
// -----------------------------------------------------------------------
|
|
62
57
|
// Blue — defined as HSL channels so we can compose hsla() halos and tints
|
|
@@ -68,16 +63,16 @@
|
|
|
68
63
|
--blue-s: 100%;
|
|
69
64
|
--blue-l: 59%;
|
|
70
65
|
|
|
71
|
-
--blue-50: hsl(var(--blue-h)
|
|
72
|
-
--blue-100: hsl(var(--blue-h)
|
|
73
|
-
--blue-200: hsl(var(--blue-h)
|
|
74
|
-
--blue-300: hsl(var(--blue-h)
|
|
75
|
-
--blue-400: hsl(var(--blue-h)
|
|
76
|
-
--blue-500: hsl(var(--blue-h)
|
|
77
|
-
--blue-600: hsl(var(--blue-h)
|
|
78
|
-
--blue-700: hsl(var(--blue-h)
|
|
79
|
-
--blue-800: hsl(var(--blue-h)
|
|
80
|
-
--blue-900: hsl(var(--blue-h)
|
|
66
|
+
--blue-50: hsl(var(--blue-h) 100% 97%);
|
|
67
|
+
--blue-100: hsl(var(--blue-h) 95% 94%);
|
|
68
|
+
--blue-200: hsl(var(--blue-h) 95% 89%);
|
|
69
|
+
--blue-300: hsl(var(--blue-h) 98% 82%);
|
|
70
|
+
--blue-400: hsl(var(--blue-h) 100% 75%);
|
|
71
|
+
--blue-500: hsl(var(--blue-h) 100% 66%);
|
|
72
|
+
--blue-600: hsl(var(--blue-h) var(--blue-s) var(--blue-l));
|
|
73
|
+
--blue-700: hsl(var(--blue-h) 85% 52%);
|
|
74
|
+
--blue-800: hsl(var(--blue-h) 79% 40%);
|
|
75
|
+
--blue-900: hsl(var(--blue-h) 77% 30%);
|
|
81
76
|
|
|
82
77
|
// -----------------------------------------------------------------------
|
|
83
78
|
// Orange — focus / secondary signal
|
|
@@ -95,6 +90,9 @@
|
|
|
95
90
|
// -----------------------------------------------------------------------
|
|
96
91
|
// Green — success
|
|
97
92
|
// -----------------------------------------------------------------------
|
|
93
|
+
// HSL channels (additive — for focus-ring composition + ds-color). The ramp
|
|
94
|
+
// stops below stay hand-picked hex; these reproduce --green-500 ≈ #22A33A.
|
|
95
|
+
--green-h: 132; --green-s: 66%; --green-l: 39%;
|
|
98
96
|
--green-50: #ECFAEE;
|
|
99
97
|
--green-100: #D6F3DB;
|
|
100
98
|
--green-200: #BBE8C2;
|
|
@@ -107,6 +105,9 @@
|
|
|
107
105
|
// -----------------------------------------------------------------------
|
|
108
106
|
// Yellow — attention / warning
|
|
109
107
|
// -----------------------------------------------------------------------
|
|
108
|
+
// HSL channels (additive — for focus-ring composition + ds-color). Reproduce
|
|
109
|
+
// --yellow-400 ≈ #F4D43A; ramp stops below stay hand-picked hex.
|
|
110
|
+
--yellow-h: 47; --yellow-s: 89%; --yellow-l: 59%;
|
|
110
111
|
--yellow-50: #FFFBEA;
|
|
111
112
|
--yellow-100: #FEF8DC;
|
|
112
113
|
--yellow-200: #FCEFA1;
|
|
@@ -124,14 +125,14 @@
|
|
|
124
125
|
--red-s: 75%;
|
|
125
126
|
--red-l: 49%;
|
|
126
127
|
|
|
127
|
-
--red-50: hsl(var(--red-h)
|
|
128
|
-
--red-100: hsl(var(--red-h)
|
|
129
|
-
--red-200: hsl(var(--red-h)
|
|
130
|
-
--red-300: hsl(var(--red-h)
|
|
131
|
-
--red-400: hsl(var(--red-h)
|
|
132
|
-
--red-500: hsl(var(--red-h)
|
|
133
|
-
--red-600: hsl(var(--red-h)
|
|
134
|
-
--red-700: hsl(var(--red-h)
|
|
128
|
+
--red-50: hsl(var(--red-h) 86% 97%);
|
|
129
|
+
--red-100: hsl(var(--red-h) 86% 95%);
|
|
130
|
+
--red-200: hsl(var(--red-h) 86% 88%);
|
|
131
|
+
--red-300: hsl(var(--red-h) 84% 76%);
|
|
132
|
+
--red-400: hsl(var(--red-h) 87% 57%);
|
|
133
|
+
--red-500: hsl(var(--red-h) var(--red-s) var(--red-l));
|
|
134
|
+
--red-600: hsl(var(--red-h) 78% 41%);
|
|
135
|
+
--red-700: hsl(var(--red-h) 78% 31%);
|
|
135
136
|
|
|
136
137
|
// -----------------------------------------------------------------------
|
|
137
138
|
// Sky — info role ramp. A brighter, cooler blue (#3B82F6) kept distinct
|
|
@@ -148,12 +149,28 @@
|
|
|
148
149
|
--sky-700: #1D4ED8;
|
|
149
150
|
|
|
150
151
|
// -----------------------------------------------------------------------
|
|
151
|
-
// Shadow tint — channel-split substrate.
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
// glows over dark surfaces.
|
|
152
|
+
// Shadow tint — channel-split substrate h/s only. The lightness (-l) is
|
|
153
|
+
// per-theme: light uses 7%, dark uses 95% (so the same alphas read as soft
|
|
154
|
+
// glows over dark surfaces). Each theme's _palette.scss mixin sets -l.
|
|
155
155
|
// -----------------------------------------------------------------------
|
|
156
156
|
--shadow-tint-h: 225;
|
|
157
157
|
--shadow-tint-s: 39%;
|
|
158
|
-
|
|
158
|
+
|
|
159
|
+
// -----------------------------------------------------------------------
|
|
160
|
+
// Theme-stable contrast anchors — never flip with the theme. On-fill text/
|
|
161
|
+
// icon roles point here so a dark theme keeps white-on-coloured-fill correct.
|
|
162
|
+
// -----------------------------------------------------------------------
|
|
163
|
+
--static-white: #FFF;
|
|
164
|
+
--static-ink: #161A28;
|
|
165
|
+
|
|
166
|
+
// -----------------------------------------------------------------------
|
|
167
|
+
// Font families — a theme/brand choice (the size/weight/leading SCALE is
|
|
168
|
+
// theme-independent and lives in styles/_tokens.scss). Display + heading
|
|
169
|
+
// default to body sans; a brand can mix a serif/display family here without
|
|
170
|
+
// touching component code.
|
|
171
|
+
// -----------------------------------------------------------------------
|
|
172
|
+
--font-sans: "Mulish", -apple-system, blinkmacsystemfont, "Segoe UI", roboto, sans-serif;
|
|
173
|
+
--font-mono: "JetBrains Mono", ui-monospace, sfmono-regular, menlo, monospace;
|
|
174
|
+
--font-display: var(--font-sans);
|
|
175
|
+
--font-heading: var(--font-sans);
|
|
159
176
|
}
|
|
@@ -1,40 +1,21 @@
|
|
|
1
1
|
// =========================================================================
|
|
2
|
-
//
|
|
2
|
+
// Shared role structure — Tier 2 semantic role ASSIGNMENTS.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// whole system. A sibling theme (e.g. dark) ships its own palette + this map.
|
|
4
|
+
// Exposes @mixin roles: the role map whose structure is identical across
|
|
5
|
+
// themes and which resolves through the per-theme palette (surfaces →
|
|
6
|
+
// --neutral-*, text/icon/border → --neutral-*, brand channels, status, focus,
|
|
7
|
+
// semantic→palette bridge). These are the LIGHT-DEFAULT values — dark theme
|
|
8
|
+
// overrides only the deltas in its own _theme.scss mixin body.
|
|
10
9
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// This mixin is an authoring-DRY device. At runtime, exactly one theme block
|
|
11
|
+
// matches (`:not([data-theme='dark'])` vs `[data-theme='dark']`), and that
|
|
12
|
+
// block declares the complete token set itself (palette + roles + overrides).
|
|
13
13
|
// =========================================================================
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
/* Neutral family slot — semantic pointer at the active gray ramp from the
|
|
17
|
-
palette. Default: hand-drawn cool-gray. <html data-neutrals="brand">
|
|
18
|
-
(ds-docs tweaks panel) repoints it at the brand-derived brand-gray ramp,
|
|
19
|
-
where --primary-h re-tints every gray. Roles below consume --neutral-*
|
|
20
|
-
so the family switch never touches them. */
|
|
21
|
-
--neutral-0: var(--cool-gray-0);
|
|
22
|
-
--neutral-50: var(--cool-gray-50);
|
|
23
|
-
--neutral-100: var(--cool-gray-100);
|
|
24
|
-
--neutral-150: var(--cool-gray-150);
|
|
25
|
-
--neutral-200: var(--cool-gray-200);
|
|
26
|
-
--neutral-300: var(--cool-gray-300);
|
|
27
|
-
--neutral-400: var(--cool-gray-400);
|
|
28
|
-
--neutral-500: var(--cool-gray-500);
|
|
29
|
-
--neutral-600: var(--cool-gray-600);
|
|
30
|
-
--neutral-700: var(--cool-gray-700);
|
|
31
|
-
--neutral-800: var(--cool-gray-800);
|
|
32
|
-
--neutral-900: var(--cool-gray-900);
|
|
33
|
-
--neutral-950: var(--cool-gray-950);
|
|
34
|
-
|
|
15
|
+
@mixin roles {
|
|
35
16
|
/* Surfaces — six-stop scale.
|
|
36
17
|
· canvas : page background
|
|
37
|
-
· default : cards, sheets, toolbars (the
|
|
18
|
+
· default : cards, sheets, toolbars (the "paper")
|
|
38
19
|
· secondary : hover lanes, table heads, addons, code blocks
|
|
39
20
|
· overlay : modals, drawers, popovers (paired with scrim)
|
|
40
21
|
· tint : brand-tinted (active rows, sidenav active item)
|
|
@@ -54,12 +35,12 @@
|
|
|
54
35
|
|
|
55
36
|
/* Scrim — modal/drawer backdrop. Derived from inverted surface
|
|
56
37
|
so dark-mode override flips automatically. */
|
|
57
|
-
--surface-scrim:
|
|
38
|
+
--surface-scrim: hsl(var(--shadow-tint-h) var(--shadow-tint-s) var(--shadow-tint-l) / 55%);
|
|
58
39
|
|
|
59
40
|
/* Visible-on-coloured-fill divider. The split-button right-hand chevron
|
|
60
41
|
sits ON the brand fill, so a transparent white hairline reads correctly
|
|
61
42
|
across any brand hue. Override per theme if the brand is too light. */
|
|
62
|
-
--btn-split-divider:
|
|
43
|
+
--btn-split-divider: hsl(0deg 0% 100% / 25%);
|
|
63
44
|
|
|
64
45
|
/* Visited link — derives from --primary-muted by default so it sympathetically
|
|
65
46
|
shifts with the brand hue. Override (e.g. for financial dashboards that
|
|
@@ -81,35 +62,28 @@
|
|
|
81
62
|
--text-muted: var(--neutral-400);
|
|
82
63
|
--text-disabled: var(--neutral-400);
|
|
83
64
|
--text-inverted: var(--neutral-0);
|
|
65
|
+
|
|
84
66
|
/* Text overlaid on arbitrary imagery (camera feeds, photos) — pairs a
|
|
85
67
|
guaranteed-light colour with a contrast shadow. */
|
|
86
|
-
--text-on-media: var(--
|
|
87
|
-
--text-on-media-shadow: 0 1px 3px
|
|
68
|
+
--text-on-media: var(--static-white);
|
|
69
|
+
--text-on-media-shadow: 0 1px 3px hsl(0deg 0% 0% / 70%);
|
|
88
70
|
--text-link: var(--blue-700);
|
|
89
|
-
--text-on-primary: var(--
|
|
90
|
-
--text-on-success: var(--
|
|
71
|
+
--text-on-primary: var(--static-white);
|
|
72
|
+
--text-on-success: var(--static-white);
|
|
73
|
+
|
|
91
74
|
/* Warning fill is yellow (--yellow-400 #F4D43A) — white text on it is only
|
|
92
75
|
~1.5:1 (fails WCAG). On-warning text is the darkest neutral (~10:1). */
|
|
93
|
-
--text-on-warning: var(--
|
|
94
|
-
--text-on-error: var(--
|
|
76
|
+
--text-on-warning: var(--static-ink);
|
|
77
|
+
--text-on-error: var(--static-white);
|
|
95
78
|
|
|
96
79
|
/* Icons */
|
|
97
80
|
--icon-default: var(--neutral-500);
|
|
98
81
|
--icon-strong: var(--neutral-700);
|
|
99
82
|
--icon-muted: var(--neutral-400);
|
|
100
|
-
--icon-on-primary: var(--
|
|
101
|
-
|
|
102
|
-
/* Font families — a theme/brand choice (the size/weight/leading SCALE is
|
|
103
|
-
theme-independent and lives in styles/_tokens.scss). Display + heading
|
|
104
|
-
default to body sans; a brand can mix a serif/display family here without
|
|
105
|
-
touching component code. */
|
|
106
|
-
--font-sans: "Mulish", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
107
|
-
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
108
|
-
--font-display: var(--font-sans);
|
|
109
|
-
--font-heading: var(--font-sans);
|
|
83
|
+
--icon-on-primary: var(--static-white);
|
|
110
84
|
|
|
111
85
|
/* Semantic → palette bridge. The lib knows colours by PURPOSE; the theme
|
|
112
|
-
(themes/
|
|
86
|
+
(themes/light/_theme.scss) ships the concrete ramps + HSL channels. This
|
|
113
87
|
is the one place that maps semantic roles onto a concrete palette. A brand
|
|
114
88
|
re-skins by overriding either the palette (--blue-*, --red-*) or these
|
|
115
89
|
role tokens directly.
|
|
@@ -120,6 +94,12 @@
|
|
|
120
94
|
--error-h: var(--red-h);
|
|
121
95
|
--error-s: var(--red-s);
|
|
122
96
|
--error-l: var(--red-l);
|
|
97
|
+
--success-h: var(--green-h);
|
|
98
|
+
--success-s: var(--green-s);
|
|
99
|
+
--success-l: var(--green-l);
|
|
100
|
+
--warning-h: var(--yellow-h);
|
|
101
|
+
--warning-s: var(--yellow-s);
|
|
102
|
+
--warning-l: var(--yellow-l);
|
|
123
103
|
|
|
124
104
|
/* Primary roles.
|
|
125
105
|
Hover / pressed / selected-hover are DERIVED from --primary at runtime via
|
|
@@ -135,7 +115,7 @@
|
|
|
135
115
|
--primary-muted: var(--blue-100);
|
|
136
116
|
--primary-muted-strong: var(--blue-200);
|
|
137
117
|
--primary-strong: var(--blue-700);
|
|
138
|
-
--primary-on: var(--
|
|
118
|
+
--primary-on: var(--static-white);
|
|
139
119
|
|
|
140
120
|
/* Accent / secondary role — composed from --secondary-h/s/l channels.
|
|
141
121
|
Channels default to brand values so unbranded apps render identical to
|
|
@@ -146,12 +126,13 @@
|
|
|
146
126
|
--secondary-s: var(--primary-s);
|
|
147
127
|
--secondary-l: var(--primary-l);
|
|
148
128
|
|
|
149
|
-
--secondary: hsl(var(--secondary-h)
|
|
129
|
+
--secondary: hsl(var(--secondary-h) var(--secondary-s) var(--secondary-l));
|
|
150
130
|
--secondary-hover: color-mix(in srgb, var(--secondary) 88%, black);
|
|
151
131
|
--secondary-pressed: color-mix(in srgb, var(--secondary) 76%, black);
|
|
152
|
-
--secondary-strong: hsl(var(--secondary-h)
|
|
153
|
-
--secondary-subtitle: hsl(var(--secondary-h)
|
|
154
|
-
--secondary-muted: hsl(var(--secondary-h)
|
|
132
|
+
--secondary-strong: hsl(var(--secondary-h) var(--secondary-s) calc(var(--secondary-l) - 11%));
|
|
133
|
+
--secondary-subtitle: hsl(var(--secondary-h) 100% 97%);
|
|
134
|
+
--secondary-muted: hsl(var(--secondary-h) 95% 94%);
|
|
135
|
+
--secondary-muted-strong: hsl(var(--secondary-h) 95% 89%);
|
|
155
136
|
--secondary-on: var(--primary-on);
|
|
156
137
|
|
|
157
138
|
/* Tertiary — third brand hue. Used by the tertiary button variant and
|
|
@@ -165,12 +146,13 @@
|
|
|
165
146
|
--tertiary-s: var(--primary-s);
|
|
166
147
|
--tertiary-l: var(--primary-l);
|
|
167
148
|
|
|
168
|
-
--tertiary: hsl(var(--tertiary-h)
|
|
149
|
+
--tertiary: hsl(var(--tertiary-h) var(--tertiary-s) var(--tertiary-l));
|
|
169
150
|
--tertiary-hover: color-mix(in srgb, var(--tertiary) 88%, black);
|
|
170
151
|
--tertiary-pressed: color-mix(in srgb, var(--tertiary) 76%, black);
|
|
171
|
-
--tertiary-strong: hsl(var(--tertiary-h)
|
|
172
|
-
--tertiary-subtitle: hsl(var(--tertiary-h)
|
|
173
|
-
--tertiary-muted: hsl(var(--tertiary-h)
|
|
152
|
+
--tertiary-strong: hsl(var(--tertiary-h) var(--tertiary-s) calc(var(--tertiary-l) - 11%));
|
|
153
|
+
--tertiary-subtitle: hsl(var(--tertiary-h) 100% 97%);
|
|
154
|
+
--tertiary-muted: hsl(var(--tertiary-h) 95% 94%);
|
|
155
|
+
--tertiary-muted-strong: hsl(var(--tertiary-h) 95% 89%);
|
|
174
156
|
--tertiary-on: var(--primary-on);
|
|
175
157
|
|
|
176
158
|
/* Status roles.
|
|
@@ -182,6 +164,7 @@
|
|
|
182
164
|
--success-hover: color-mix(in srgb, var(--success) 88%, black);
|
|
183
165
|
--success-subtitle: var(--green-50);
|
|
184
166
|
--success-muted: var(--green-100);
|
|
167
|
+
--success-muted-strong: var(--green-200);
|
|
185
168
|
--success-border: var(--green-200);
|
|
186
169
|
--success-text: var(--green-700);
|
|
187
170
|
|
|
@@ -189,6 +172,7 @@
|
|
|
189
172
|
--warning-hover: color-mix(in srgb, var(--warning) 88%, black);
|
|
190
173
|
--warning-subtitle: var(--yellow-50);
|
|
191
174
|
--warning-muted: var(--yellow-100);
|
|
175
|
+
--warning-muted-strong: var(--yellow-200);
|
|
192
176
|
--warning-border: var(--yellow-200);
|
|
193
177
|
--warning-text: var(--yellow-700);
|
|
194
178
|
|
|
@@ -197,9 +181,18 @@
|
|
|
197
181
|
--error-pressed: color-mix(in srgb, var(--error) 76%, black);
|
|
198
182
|
--error-subtitle: var(--red-50);
|
|
199
183
|
--error-muted: var(--red-100);
|
|
184
|
+
--error-muted-strong: var(--red-200);
|
|
200
185
|
--error-border: var(--red-200);
|
|
201
186
|
--error-text: var(--red-600);
|
|
202
187
|
|
|
188
|
+
/* -pressed (success/warning) + -on aliases — added for the --accent-* pointer
|
|
189
|
+
family so every ds-color family resolves --accent-pressed / --accent-on. */
|
|
190
|
+
--success-pressed: color-mix(in srgb, var(--success) 76%, black);
|
|
191
|
+
--warning-pressed: color-mix(in srgb, var(--warning) 76%, black);
|
|
192
|
+
--error-on: var(--text-on-error);
|
|
193
|
+
--success-on: var(--text-on-success);
|
|
194
|
+
--warning-on: var(--text-on-warning);
|
|
195
|
+
|
|
203
196
|
--info: var(--sky-500);
|
|
204
197
|
--info-hover: color-mix(in srgb, var(--info) 88%, black);
|
|
205
198
|
--info-subtitle: var(--sky-50);
|
|
@@ -212,17 +205,18 @@
|
|
|
212
205
|
brand re-skin moves Confirmed with it. The other lifecycle
|
|
213
206
|
stages are deliberately neutral/green/dark to keep semantic
|
|
214
207
|
distance from brand. */
|
|
208
|
+
|
|
215
209
|
/* Status pills — canonical (single system).
|
|
216
210
|
Legacy classes `.badge--active` (single) and ad-hoc semantic
|
|
217
211
|
pills are unified in the Badge component to consume these tokens. */
|
|
218
|
-
--status-planning-bg: var(--neutral-150);
|
|
219
|
-
--status-planning-
|
|
220
|
-
--status-confirmed-bg: var(--primary-subtitle);
|
|
221
|
-
--status-confirmed-
|
|
222
|
-
--status-active-bg: var(--green-50);
|
|
223
|
-
--status-active-
|
|
224
|
-
--status-completed-bg: var(--neutral-900);
|
|
225
|
-
--status-completed-
|
|
212
|
+
--status-planning-bg-color: var(--neutral-150);
|
|
213
|
+
--status-planning-text-color: var(--neutral-700);
|
|
214
|
+
--status-confirmed-bg-color: var(--primary-subtitle);
|
|
215
|
+
--status-confirmed-text-color: var(--primary-strong);
|
|
216
|
+
--status-active-bg-color: var(--green-50);
|
|
217
|
+
--status-active-text-color: var(--green-700);
|
|
218
|
+
--status-completed-bg-color: var(--neutral-900);
|
|
219
|
+
--status-completed-text-color: var(--neutral-0);
|
|
226
220
|
|
|
227
221
|
/* Focus rings.
|
|
228
222
|
Outer halos use hsla() composed from brand/danger HSL channels
|
|
@@ -237,38 +231,60 @@
|
|
|
237
231
|
|
|
238
232
|
--focus-ring:
|
|
239
233
|
0 0 0 2px var(--focus-ring-inner),
|
|
240
|
-
0 0 0 4px
|
|
234
|
+
0 0 0 4px hsl(var(--focus-ring-outer-h) var(--focus-ring-outer-s) var(--focus-ring-outer-l) / var(--focus-ring-outer-alpha));
|
|
241
235
|
--focus-ring-inverted:
|
|
242
236
|
0 0 0 2px var(--neutral-900),
|
|
243
237
|
0 0 0 4px var(--blue-300);
|
|
244
238
|
--focus-ring-error:
|
|
245
239
|
0 0 0 2px var(--focus-ring-inner),
|
|
246
|
-
0 0 0 4px
|
|
240
|
+
0 0 0 4px hsl(var(--error-h) var(--error-s) var(--error-l) / 100%);
|
|
247
241
|
|
|
248
242
|
/* Field focus — coloured halo only (no double-ring). Derived from
|
|
249
243
|
brand HSL so it tracks rebrand. Alpha is a token so dark themes
|
|
250
244
|
can boost it for legibility. */
|
|
251
245
|
--focus-field-alpha: .22;
|
|
252
246
|
--focus-field:
|
|
253
|
-
0 0 0 3px
|
|
247
|
+
0 0 0 3px hsl(var(--primary-h) var(--primary-s) var(--primary-l) / var(--focus-field-alpha));
|
|
254
248
|
--focus-field-error:
|
|
255
|
-
0 0 0 3px
|
|
256
|
-
|
|
249
|
+
0 0 0 3px hsl(var(--error-h) var(--error-s) var(--error-l) / var(--focus-field-alpha));
|
|
250
|
+
|
|
251
|
+
/* Per-family focus rings/fields — pre-composed once from each family's
|
|
252
|
+
channels so ds-color flips a single --accent-focus-* pointer (no :root
|
|
253
|
+
composite re-declaration in the scope). --focus-ring-error exists above. */
|
|
254
|
+
--focus-ring-secondary:
|
|
255
|
+
0 0 0 2px var(--focus-ring-inner),
|
|
256
|
+
0 0 0 4px hsl(var(--secondary-h) var(--secondary-s) var(--secondary-l) / 100%);
|
|
257
|
+
--focus-ring-tertiary:
|
|
258
|
+
0 0 0 2px var(--focus-ring-inner),
|
|
259
|
+
0 0 0 4px hsl(var(--tertiary-h) var(--tertiary-s) var(--tertiary-l) / 100%);
|
|
260
|
+
--focus-ring-success:
|
|
261
|
+
0 0 0 2px var(--focus-ring-inner),
|
|
262
|
+
0 0 0 4px hsl(var(--success-h) var(--success-s) var(--success-l) / 100%);
|
|
263
|
+
--focus-ring-warning:
|
|
264
|
+
0 0 0 2px var(--focus-ring-inner),
|
|
265
|
+
0 0 0 4px hsl(var(--warning-h) var(--warning-s) var(--warning-l) / 100%);
|
|
266
|
+
--focus-field-secondary:
|
|
267
|
+
0 0 0 3px hsl(var(--secondary-h) var(--secondary-s) var(--secondary-l) / var(--focus-field-alpha));
|
|
268
|
+
--focus-field-tertiary:
|
|
269
|
+
0 0 0 3px hsl(var(--tertiary-h) var(--tertiary-s) var(--tertiary-l) / var(--focus-field-alpha));
|
|
270
|
+
--focus-field-success:
|
|
271
|
+
0 0 0 3px hsl(var(--success-h) var(--success-s) var(--success-l) / var(--focus-field-alpha));
|
|
272
|
+
--focus-field-warning:
|
|
273
|
+
0 0 0 3px hsl(var(--warning-h) var(--warning-s) var(--warning-l) / var(--focus-field-alpha));
|
|
257
274
|
|
|
258
|
-
/*
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
--
|
|
263
|
-
--
|
|
264
|
-
--
|
|
265
|
-
--
|
|
266
|
-
--
|
|
267
|
-
--
|
|
268
|
-
--
|
|
269
|
-
--
|
|
270
|
-
--
|
|
271
|
-
--
|
|
272
|
-
--
|
|
273
|
-
--neutral-950: var(--brand-gray-950);
|
|
275
|
+
/* Accent pointer layer — the single indirection ds-color flips. Defaults to
|
|
276
|
+
the primary family (+ existing focus/tint tokens) so the unbranded theme is
|
|
277
|
+
pixel-identical. Components read these for their accent instead of
|
|
278
|
+
--primary* / --focus-* / --surface-tint. */
|
|
279
|
+
--accent: var(--primary);
|
|
280
|
+
--accent-hover: var(--primary-hover);
|
|
281
|
+
--accent-pressed: var(--primary-pressed);
|
|
282
|
+
--accent-strong: var(--primary-strong);
|
|
283
|
+
--accent-subtitle: var(--primary-subtitle);
|
|
284
|
+
--accent-muted: var(--primary-muted);
|
|
285
|
+
--accent-muted-strong: var(--primary-muted-strong);
|
|
286
|
+
--accent-on: var(--primary-on);
|
|
287
|
+
--accent-focus-ring: var(--focus-ring);
|
|
288
|
+
--accent-focus-field: var(--focus-field);
|
|
289
|
+
--accent-surface-tint: var(--surface-tint);
|
|
274
290
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// =========================================================================
|
|
2
|
+
// Dark palette — Tier 1b per-theme primitives.
|
|
3
|
+
//
|
|
4
|
+
// Exposes @mixin palette with ONLY the dark-specific primitives:
|
|
5
|
+
// · --neutral-* slot: hand-tuned dark neutrals (NOT a linear inverse —
|
|
6
|
+
// note --neutral-0 is an ELEVATED card surface, LIGHTER than the
|
|
7
|
+
// --neutral-50 canvas — the opposite of light, where 0 (#FFF) is the
|
|
8
|
+
// brightest stop). Brand/status ramps are left to the role layer.
|
|
9
|
+
// · --shadow-tint-l: 95% (flipped so same alphas read as soft glows)
|
|
10
|
+
//
|
|
11
|
+
// Theme-independent ramps (--cool-gray-*, --brand-gray-*, --blue-*, etc.)
|
|
12
|
+
// and HSL channels live in themes/_base-palette.scss and emit at :root.
|
|
13
|
+
//
|
|
14
|
+
// KNOWN LIMITATION — data-neutrals="brand" + dark: this ramp overrides
|
|
15
|
+
// --neutral-* directly, so when both `[data-neutrals='brand']` and
|
|
16
|
+
// `[data-theme='dark']` are set (equal specificity) dark wins by source order —
|
|
17
|
+
// the brand-gray hue-tint is dropped and these fixed dark neutrals apply. Benign
|
|
18
|
+
// (renders correctly dark, just not brand-hue-tinted); a dark brand-gray ramp is
|
|
19
|
+
// a future follow-up, not v1.
|
|
20
|
+
// =========================================================================
|
|
21
|
+
|
|
22
|
+
@mixin palette {
|
|
23
|
+
--neutral-0: #171B24; // surface-default / overlay (cards) — elevated
|
|
24
|
+
--neutral-50: #0F131A; // surface-canvas — darkest (page bg)
|
|
25
|
+
--neutral-100: #1E2330; // surface-secondary / disabled
|
|
26
|
+
--neutral-150: #242A38; // surface-muted, border-subtitle/divider/disabled
|
|
27
|
+
--neutral-200: #2A3040; // border-default
|
|
28
|
+
--neutral-300: #3A4252; // border-strong
|
|
29
|
+
--neutral-400: #6B7388; // text-muted/disabled, icon-muted
|
|
30
|
+
--neutral-500: #8A93A6; // text-tertiary, icon-default
|
|
31
|
+
--neutral-600: #9BA3B4;
|
|
32
|
+
--neutral-700: #B7BFD0; // text-secondary, icon-strong, border-inverted
|
|
33
|
+
--neutral-800: #CDD3E0;
|
|
34
|
+
--neutral-900: #E7EAF2; // text-primary, surface-inverted
|
|
35
|
+
--neutral-950: #F4F6FA;
|
|
36
|
+
|
|
37
|
+
// Shadow substrate flips light so the same alphas read as soft glows on dark.
|
|
38
|
+
--shadow-tint-l: 95%;
|
|
39
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// =========================================================================
|
|
2
|
+
// Dark theme — Tier 2 role overrides.
|
|
3
|
+
//
|
|
4
|
+
// Composes the dark palette (neutral ramp + shadow-l 95%) with the shared
|
|
5
|
+
// role structure, then overrides only the roles whose VALUES differ from
|
|
6
|
+
// the light defaults. The activation selector (:root[data-theme='dark'])
|
|
7
|
+
// lives in styles/_theme-activation.scss. Dark is opt-in — light is the
|
|
8
|
+
// default; there is no automatic prefers-color-scheme switch.
|
|
9
|
+
//
|
|
10
|
+
// Most roles follow the inverted --neutral-* ramp automatically (custom
|
|
11
|
+
// props resolve at use-time). This file overrides ONLY roles that can't
|
|
12
|
+
// follow the flip: tint/subtle surfaces (near-white *-50 picks → dark hue
|
|
13
|
+
// tints), on-tint text (*-text / *-strong → lighter stops), hover/pressed
|
|
14
|
+
// direction, scrim (must stay dark), focus halo alpha, and charts.
|
|
15
|
+
// On-fill text already points at theme-stable --static-white/--static-ink.
|
|
16
|
+
// =========================================================================
|
|
17
|
+
|
|
18
|
+
@use 'palette';
|
|
19
|
+
@use '../semantic';
|
|
20
|
+
|
|
21
|
+
@mixin theme {
|
|
22
|
+
@include palette.palette;
|
|
23
|
+
@include semantic.roles;
|
|
24
|
+
|
|
25
|
+
// Interaction states: on dark, hover/pressed must LIGHTEN. The default theme
|
|
26
|
+
// derives them as color-mix toward black (darkens) — wrong direction here.
|
|
27
|
+
// Mirror the light recipe's 88% / 76% steps, mixed toward white instead.
|
|
28
|
+
--primary-hover: color-mix(in srgb, var(--primary) 88%, white);
|
|
29
|
+
--primary-pressed: color-mix(in srgb, var(--primary) 76%, white);
|
|
30
|
+
--primary-selected-hover: color-mix(in srgb, var(--primary) 88%, white);
|
|
31
|
+
--secondary-hover: color-mix(in srgb, var(--secondary) 88%, white);
|
|
32
|
+
--secondary-pressed: color-mix(in srgb, var(--secondary) 76%, white);
|
|
33
|
+
--tertiary-hover: color-mix(in srgb, var(--tertiary) 88%, white);
|
|
34
|
+
--tertiary-pressed: color-mix(in srgb, var(--tertiary) 76%, white);
|
|
35
|
+
--success-hover: color-mix(in srgb, var(--success) 88%, white);
|
|
36
|
+
--warning-hover: color-mix(in srgb, var(--warning) 88%, white);
|
|
37
|
+
--error-hover: color-mix(in srgb, var(--error) 88%, white);
|
|
38
|
+
--error-pressed: color-mix(in srgb, var(--error) 76%, white);
|
|
39
|
+
--success-pressed: color-mix(in srgb, var(--success) 76%, white);
|
|
40
|
+
--warning-pressed: color-mix(in srgb, var(--warning) 76%, white);
|
|
41
|
+
--info-hover: color-mix(in srgb, var(--info) 88%, white);
|
|
42
|
+
|
|
43
|
+
// Brand tint surfaces + on-tint text (channel-derived → track rebrand).
|
|
44
|
+
--surface-tint: hsl(var(--primary-h) 45% 16%);
|
|
45
|
+
--primary-subtitle: hsl(var(--primary-h) 45% 18%);
|
|
46
|
+
--primary-muted: hsl(var(--primary-h) 40% 26%);
|
|
47
|
+
--primary-muted-strong: hsl(var(--primary-h) 42% 32%);
|
|
48
|
+
--primary-strong: hsl(var(--primary-h) 90% 72%);
|
|
49
|
+
|
|
50
|
+
--secondary-subtitle: hsl(var(--secondary-h) 45% 18%);
|
|
51
|
+
--secondary-muted: hsl(var(--secondary-h) 40% 26%);
|
|
52
|
+
--secondary-muted-strong: hsl(var(--secondary-h) 42% 32%);
|
|
53
|
+
--secondary-strong: hsl(var(--secondary-h) 90% 72%);
|
|
54
|
+
|
|
55
|
+
--tertiary-subtitle: hsl(var(--tertiary-h) 45% 18%);
|
|
56
|
+
--tertiary-muted: hsl(var(--tertiary-h) 40% 26%);
|
|
57
|
+
--tertiary-muted-strong: hsl(var(--tertiary-h) 42% 32%);
|
|
58
|
+
--tertiary-strong: hsl(var(--tertiary-h) 90% 72%);
|
|
59
|
+
|
|
60
|
+
--text-link: hsl(var(--primary-h) 90% 74%);
|
|
61
|
+
--link-visited: hsl(var(--primary-h) 50% 70%);
|
|
62
|
+
|
|
63
|
+
// Status families: dark tint bg + border, lighter text.
|
|
64
|
+
--success-subtitle: hsl(132deg 38% 14%);
|
|
65
|
+
--success-muted: hsl(132deg 34% 20%);
|
|
66
|
+
--success-muted-strong: hsl(132deg 30% 28%);
|
|
67
|
+
--success-border: hsl(132deg 30% 28%);
|
|
68
|
+
--success-text: hsl(132deg 55% 62%);
|
|
69
|
+
|
|
70
|
+
--warning-subtitle: hsl(45deg 42% 14%);
|
|
71
|
+
--warning-muted: hsl(45deg 38% 22%);
|
|
72
|
+
--warning-muted-strong: hsl(45deg 35% 30%);
|
|
73
|
+
--warning-border: hsl(45deg 35% 30%);
|
|
74
|
+
--warning-text: hsl(45deg 90% 62%);
|
|
75
|
+
|
|
76
|
+
--error-subtitle: hsl(0deg 45% 16%);
|
|
77
|
+
--error-muted: hsl(0deg 40% 24%);
|
|
78
|
+
--error-muted-strong: hsl(0deg 38% 32%);
|
|
79
|
+
--error-border: hsl(0deg 38% 32%);
|
|
80
|
+
--error-text: hsl(0deg 85% 74%);
|
|
81
|
+
|
|
82
|
+
--info-subtitle: hsl(217deg 45% 16%);
|
|
83
|
+
--info-muted: hsl(217deg 40% 24%);
|
|
84
|
+
--info-border: hsl(217deg 38% 32%);
|
|
85
|
+
--info-text: hsl(217deg 90% 74%);
|
|
86
|
+
|
|
87
|
+
// Status pills that referenced palette stops directly (green-50/700) — repoint
|
|
88
|
+
// to the dark status roles so the lifecycle pill reads on dark. (planning →
|
|
89
|
+
// neutral, confirmed → primary-*, completed → neutral all follow flips.)
|
|
90
|
+
--status-active-bg-color: var(--success-subtitle);
|
|
91
|
+
--status-active-text-color: var(--success-text);
|
|
92
|
+
|
|
93
|
+
// Scrim stays dark — must NOT follow --shadow-tint-l: 95%.
|
|
94
|
+
--surface-scrim: hsl(225deg 39% 4% / 70%);
|
|
95
|
+
|
|
96
|
+
// Field focus halo — boost alpha for legibility on dark.
|
|
97
|
+
--focus-field-alpha: .35;
|
|
98
|
+
|
|
99
|
+
// Chart / dataviz overrides. Axis/grid/label/title/cursor already flip via the
|
|
100
|
+
// --neutral-* / --text-* roles; only the light-bg-tuned bits move here.
|
|
101
|
+
--chart-hover-bg-color: hsl(0deg 0% 100% / 6%); // light wash (default dark wash is invisible on dark)
|
|
102
|
+
--chart-tooltip-bg-color: var(--surface-overlay); // native dark tooltip, not an inverted light box
|
|
103
|
+
--chart-tooltip-text-color: var(--text-primary);
|
|
104
|
+
--chart-tooltip-border: var(--border-strong);
|
|
105
|
+
|
|
106
|
+
// Sequential — flip so low → high reads dark → bright on a dark canvas.
|
|
107
|
+
--chart-seq-1: var(--blue-800);
|
|
108
|
+
--chart-seq-2: var(--blue-700);
|
|
109
|
+
--chart-seq-3: var(--blue-600);
|
|
110
|
+
--chart-seq-4: var(--blue-500);
|
|
111
|
+
--chart-seq-5: var(--blue-400);
|
|
112
|
+
--chart-seq-6: var(--blue-300);
|
|
113
|
+
|
|
114
|
+
// Divergent — bright red ← dark-neutral zero → bright green (|value| → brightness).
|
|
115
|
+
--chart-div--3: hsl(0deg 70% 62%);
|
|
116
|
+
--chart-div--2: hsl(0deg 55% 46%);
|
|
117
|
+
--chart-div--1: hsl(0deg 45% 32%);
|
|
118
|
+
--chart-div-0: var(--neutral-150);
|
|
119
|
+
--chart-div-1: hsl(140deg 40% 32%);
|
|
120
|
+
--chart-div-2: hsl(140deg 48% 46%);
|
|
121
|
+
--chart-div-3: hsl(140deg 55% 60%);
|
|
122
|
+
}
|