@flyos/design-system 1.7.0 → 2.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/fesm2022/flyos-design-system.mjs +2948 -575
- package/fesm2022/flyos-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/scss/_app-surface-tokens.scss +228 -13
- package/scss/_app-surface-utilities.scss +7 -2
- package/scss/_business-app-buttons.scss +35 -8
- package/scss/_fly-theme.scss +9 -0
- package/scss/{_vos-button-mixins.scss → _fos-button-mixins.scss} +28 -28
- package/scss/_ink-baseline.scss +29 -29
- package/scss/_nova-glass.scss +451 -0
- package/scss/_nova-motion-mixins.scss +42 -0
- package/scss/_nova-motion.scss +324 -0
- package/scss/_nova-tokens.scss +356 -0
- package/scss/_nova-type.scss +188 -0
- package/scss/_shell-embed-bridge.scss +120 -81
- package/scss/_theme-dark.scss +11 -1
- package/scss/_theme-light.scss +31 -10
- package/scss/_tokens.scss +17 -9
- package/types/flyos-design-system.d.ts +1642 -36
- package/types/flyos-design-system.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
// ADDITIVE ONLY. `tools/ds-compat` classifies re-valuing an existing theme
|
|
24
24
|
// token as MAJOR (it forks the Native Federation shared singleton for every
|
|
25
25
|
// remote), so nothing here may redefine a token another DS partial owns.
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
// Three tokens are deliberately ABSENT — they are the consuming app's BRAND and
|
|
28
28
|
// every app already sets them in its own theme file:
|
|
29
29
|
// --accent --accent-soft --focus-ring
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
// The other nine platform tokens (--danger* --warning* --purple --scrim
|
|
32
32
|
// --glass-blur) USED to be absent too, on the assumption that the platform layer
|
|
33
33
|
// supplies them. That assumption only holds when an app runs FEDERATED, where
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
// commented out in both Circles and Thoughts — so `var(--danger)` resolved to
|
|
37
37
|
// nothing and error text rendered colourless. Confirmed in a live browser, not
|
|
38
38
|
// inferred: 9 of the 12 computed to the empty string on Thoughts standalone.
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
// They are now mirrored below at the SAME values `_theme-light/dark.scss` uses.
|
|
41
41
|
// Mirroring (rather than picking new values) is what makes this safe under the
|
|
42
42
|
// cascade: this file's `:root, html.light-theme` selector can tie with the
|
|
@@ -47,6 +47,107 @@
|
|
|
47
47
|
// `_fly-theme.scss`, so `@use 'fly-theme'` is all a consumer needs.
|
|
48
48
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
49
|
|
|
50
|
+
// ── The platform TYPE layer (UX v2, S1.7–S1.10 fallout) ──────────────────────
|
|
51
|
+
// `_tokens.scss` owns `--font-family` / `--font-family-mono` and the `--font-*`
|
|
52
|
+
// role ramp (`--font-footnote`, `--font-caption1`, …). Like the two Nova
|
|
53
|
+
// partials below it was reachable only through `_fly-theme.scss`, i.e. only in
|
|
54
|
+
// the desktop shell.
|
|
55
|
+
|
|
56
|
+
// The type layer fails harder than the colour layers do, and it fails silently.
|
|
57
|
+
// The Nova form/overlay/content tasks landed 26 declarations across `lib/**` of
|
|
58
|
+
// the form `font: 600 13px/1.3 var(--font-family)` or `font: var(--nova-font-
|
|
59
|
+
// menu-item)`. A `var()` naming an undefined custom property is invalid at
|
|
60
|
+
// computed-value time, and in a SHORTHAND that invalidates the WHOLE
|
|
61
|
+
// declaration — weight, size and line-height all fall back to inherited, not
|
|
62
|
+
// just the family. Standalone that is a 13px control label rendering at the
|
|
63
|
+
// app's 16px body size inside a box hard-sized to 36/30/26px: a `fly-select`
|
|
64
|
+
// trigger, a `fly-form-field` label, a `fly-checkbox` label, a `fly-drawer`
|
|
65
|
+
// title. Nothing reported it because the shell renders every one of them
|
|
66
|
+
// correctly. Before this line the DS had ZERO `var(--font-family)` references
|
|
67
|
+
// in `lib/**`; the guard that now walks those files lives in
|
|
68
|
+
// `app-surface-nova-bridge.spec.ts`.
|
|
69
|
+
|
|
70
|
+
// Idempotent in the shell for the same reason as the two below: `_fly-theme.scss`
|
|
71
|
+
// already `@use`s `tokens` FIRST, Sass loads a module once per compilation, and
|
|
72
|
+
// the compiled entry is byte-identical either way.
|
|
73
|
+
|
|
74
|
+
// Standalone it adds 120 greenfield names, of which the three External Apps
|
|
75
|
+
// reference five. Four of those (`--font-title2`, `--system-blue`,
|
|
76
|
+
// `--system-indigo`, `--system-red`) were dangling references that now resolve.
|
|
77
|
+
// The fifth is `--font-family` itself, and it is the one behavioural change in
|
|
78
|
+
// this commit: Circles already loads the DS's own Geist from its
|
|
79
|
+
// `public/ui-fonts/geist.css` and is unaffected, but Thoughts and PPM write
|
|
80
|
+
// `var(--font-family, 'Roboto', …)` on `html, body` while bundling Roboto and
|
|
81
|
+
// NOT Geist. Both must bind `--font-family` to their own stack (or drop the
|
|
82
|
+
// indirection) before taking this DS release, or their body text falls through
|
|
83
|
+
// to a face they do not ship. Recorded here rather than worked around: the
|
|
84
|
+
// alternative — leaving the token undefined so their fallback keeps firing —
|
|
85
|
+
// is what dropped the 26 declarations above.
|
|
86
|
+
@use 'tokens';
|
|
87
|
+
|
|
88
|
+
// ── The Nova bridge (UX v2, S1.11) ───────────────────────────────────────────
|
|
89
|
+
// `_nova-tokens.scss` carries the Nova vocabulary — the `--w*` ink ramp,
|
|
90
|
+
// `--mat-*`, `--sys-*`, `--tint-*`, the `--glass2-*` quartet. `_fly-theme.scss`
|
|
91
|
+
// loads it, and the desktop shell is the only consumer that loads that entry.
|
|
92
|
+
// The three External Apps do NOT: Circles, Thoughts and PPM each `@use` THIS
|
|
93
|
+
// partial by bare name, and Circles keeps `// @use 'fly-theme';` commented out
|
|
94
|
+
// on line 1 of its `styles.scss` — the same fact this file's header records.
|
|
95
|
+
|
|
96
|
+
// So without the line below, the Nova names are undefined in precisely the
|
|
97
|
+
// builds that render this family on its own opaque paper. That matters for what
|
|
98
|
+
// comes NEXT rather than for anything today: at the DS 2.0 flip the values in
|
|
99
|
+
// this file are re-pointed at ramp steps (`--ink: var(--w95)`,
|
|
100
|
+
// `--line: var(--w18)`; the whole table is in `.workflow/plans/ux-refresh/
|
|
101
|
+
// notes/S1.11-app-surface-bridge.md`). A `var()` whose token is undefined makes
|
|
102
|
+
// the entire declaration invalid at computed-value time, so that flip would
|
|
103
|
+
// silently unset ink and hairlines for every standalone External App while
|
|
104
|
+
// looking perfect in the shell — the light-mode-only failure that
|
|
105
|
+
// `_shell-embed-bridge.scss` and `_ink-baseline.scss` were each written to end,
|
|
106
|
+
// arriving a third time through a different door.
|
|
107
|
+
|
|
108
|
+
// It is additive in both directions. In the shell it changes nothing at all:
|
|
109
|
+
// `_fly-theme.scss` already `@use`s `nova-tokens` first, Sass loads a module
|
|
110
|
+
// once per compilation, and the compiled entry is byte-identical either way.
|
|
111
|
+
// Standalone it adds greenfield names only — no Nova name is referenced or
|
|
112
|
+
// declared anywhere in Circles, Thoughts or PPM. It is also invisible to
|
|
113
|
+
// `tools/ds-compat`, which derives the token surface by scanning `src/scss/**`
|
|
114
|
+
// per file, not by compiling this entry.
|
|
115
|
+
|
|
116
|
+
// `app-surface-nova-bridge.spec.ts` holds the wire in place and proves that
|
|
117
|
+
// every `var()` this file emits still resolves inside the standalone bundle.
|
|
118
|
+
@use 'nova-tokens';
|
|
119
|
+
|
|
120
|
+
// The same argument, for motion. The app-surface components now reach for the
|
|
121
|
+
// Nova keyframes (`itemIn` / `riseIn` / `appTileIn`) and the `--nova-ease-*` /
|
|
122
|
+
// `--nova-duration-*` tokens, and those live in `_nova-motion.scss` — which,
|
|
123
|
+
// like `_nova-tokens.scss`, was reachable only through `_fly-theme.scss` and so
|
|
124
|
+
// only in the shell. Standalone, an `animation: itemIn …` naming an undefined
|
|
125
|
+
// keyframe is not an error: it silently does nothing, so a list that staggers
|
|
126
|
+
// in for a Core App would appear instantly in an External App with no gate
|
|
127
|
+
// anywhere reporting a difference. Bridging it here is what makes the two
|
|
128
|
+
// builds agree. Same idempotence: the shell already loads this module, so the
|
|
129
|
+
// compiled shell entry is unchanged, and standalone it adds one `:root` block,
|
|
130
|
+
// the keyframe inventory and the single `prefers-reduced-motion` block.
|
|
131
|
+
@use 'nova-motion';
|
|
132
|
+
|
|
133
|
+
// The same argument again, for the Nova TYPE roles — and the reason `tokens`
|
|
134
|
+
// above is not optional. Half the `--nova-font-*` table SNAPs onto the platform
|
|
135
|
+
// ramp by reference (`--nova-font-menu-item: var(--font-footnote)`,
|
|
136
|
+
// `--nova-font-body`, `--nova-font-tooltip`), so loading this partial without
|
|
137
|
+
// `tokens` would not fix anything: it would move the dangling reference one
|
|
138
|
+
// level down, where a component's `font: var(--nova-font-menu-item)` still
|
|
139
|
+
// resolves to an invalid value and still drops whole. The two travel together
|
|
140
|
+
// or neither is worth loading.
|
|
141
|
+
|
|
142
|
+
// This is the one `@use` in this file that the partial's own header argues
|
|
143
|
+
// against ("`@use`d exactly once, from `_fly-theme.scss` … never a second
|
|
144
|
+
// time"). That warning is about COMPONENT-level `@use`, which re-emits the
|
|
145
|
+
// `:root` block into each component's compiled stylesheet; this is a global
|
|
146
|
+
// entry, the same role `_fly-theme.scss` plays, and Sass still emits the module
|
|
147
|
+
// exactly once per compilation. Federated it is `_fly-theme.scss`'s copy;
|
|
148
|
+
// standalone it is this one. Never both.
|
|
149
|
+
@use 'nova-type';
|
|
150
|
+
|
|
50
151
|
:root,
|
|
51
152
|
html.light-theme {
|
|
52
153
|
// ── Neutrals — text ──────────────────────────────────────────────────────
|
|
@@ -60,7 +161,7 @@ html.light-theme {
|
|
|
60
161
|
// Label for a fill of `--ink` itself (the primary button). Distinct from
|
|
61
162
|
// `--ink-inverse`, which is a FIXED near-white for coloured/gradient fills and
|
|
62
163
|
// therefore wrong here: `--ink` flips per theme, so its label must flip too.
|
|
63
|
-
|
|
164
|
+
|
|
64
165
|
// CONTRACT — anything that redefines `--ink` MUST redefine `--on-ink`.
|
|
65
166
|
// It must also be OPAQUE. The shell-embed glass remaps in Circles and Thoughts
|
|
66
167
|
// set `--ink` to white and every surface token to a translucent veil; a
|
|
@@ -105,14 +206,22 @@ html.light-theme {
|
|
|
105
206
|
--on-accent-soft: var(--ink);
|
|
106
207
|
|
|
107
208
|
// 2. --accent-fill is a SOLID plate we own outright, so its ink is knowable
|
|
108
|
-
// regardless of backdrop —
|
|
109
|
-
// their contrast cannot be broken by a
|
|
209
|
+
// regardless of backdrop — knowable FROM THE PLATE, not "always white".
|
|
210
|
+
// Badges/chips use this pair, so their contrast cannot be broken by a
|
|
211
|
+
// host token override.
|
|
110
212
|
--accent-fill: var(--accent);
|
|
111
213
|
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
214
|
+
// FALLBACK RUNG ONLY. The accent-aware derivation at the foot of this file
|
|
215
|
+
// overrides this wherever relative colour syntax parses; this is what an
|
|
216
|
+
// older engine gets, and Nova's theme-invariant `--on-accent` (#fff) is the
|
|
217
|
+
// right answer for the platform violet in both themes.
|
|
218
|
+
|
|
219
|
+
// It is deliberately NOT an alias of `--ink-inverse` any more. Two tokens that
|
|
220
|
+
// happen to agree today are not the same requirement: "ink on a dark/gradient
|
|
221
|
+
// fill" is a constant, "ink on the accent plate" is a function of the accent.
|
|
222
|
+
// Aliasing them made the second one's contrast depend on a token that is not
|
|
223
|
+
// the plate. See the foot of the file for what it depends on now.
|
|
224
|
+
--on-accent-fill: var(--on-accent);
|
|
116
225
|
|
|
117
226
|
// ── Interaction state layers ─────────────────────────────────────────────
|
|
118
227
|
// Translucent veils layered OVER a surface for hover/press/selected instead
|
|
@@ -263,6 +372,7 @@ html.dark-theme {
|
|
|
263
372
|
--ink-3: oklch(62% 0.005 250deg);
|
|
264
373
|
--ink-4: oklch(45% 0.005 250deg);
|
|
265
374
|
--ink-hover: oklch(85% 0.005 250deg);
|
|
375
|
+
|
|
266
376
|
// Flips with --ink above: the primary button is a near-white pill here, so its
|
|
267
377
|
// label goes near-black. Aliased to --bg (the darkest opaque surface in this
|
|
268
378
|
// theme) rather than repeating its literal — see the contract on the :root decl.
|
|
@@ -324,17 +434,17 @@ html.dark-theme {
|
|
|
324
434
|
// and each reference carries a hardcoded literal fallback, e.g.
|
|
325
435
|
// `var(--label-primary, #1d1d1f)` or `var(--surface-card, #fff)`. 211 such
|
|
326
436
|
// fallbacks exist across the library.
|
|
327
|
-
|
|
437
|
+
|
|
328
438
|
// Those literals are theme-BLIND: a single constant picked by whoever wrote the
|
|
329
439
|
// component. Federated that never shows, because the desktop shell defines the
|
|
330
440
|
// whole chrome family. Standalone, no one does — so a business app got whatever
|
|
331
441
|
// constant each component happened to hardcode, in BOTH light and dark.
|
|
332
|
-
|
|
442
|
+
|
|
333
443
|
// Aliasing the chrome names onto the app-surface family fixes all 211 sites at
|
|
334
444
|
// once, without touching a single component: the var() resolves through the
|
|
335
445
|
// token and never reaches its literal. The targets are theme-aware, so these
|
|
336
446
|
// follow light/dark automatically.
|
|
337
|
-
|
|
447
|
+
|
|
338
448
|
// SELECTOR IS LOAD-BEARING: this block is `:root` ALONE — deliberately not
|
|
339
449
|
// `:root, html.light-theme` like the blocks above. The shell declares every one
|
|
340
450
|
// of these under `html.light-theme` / `html.dark-theme` (specificity 0,1,1),
|
|
@@ -364,3 +474,108 @@ html.dark-theme {
|
|
|
364
474
|
--surface-hover: var(--bg-hover);
|
|
365
475
|
--surface-active: var(--state-selected);
|
|
366
476
|
}
|
|
477
|
+
|
|
478
|
+
// ── Plate ink, derived from the plate (UX v2, S7.1a) ─────────────────────────
|
|
479
|
+
// `--on-accent-fill` labels a SOLID `--accent-fill` plate: `fly-detail-shell`'s
|
|
480
|
+
// active tab, `badge()`'s default, `fly-pagination`'s current-page pill.
|
|
481
|
+
|
|
482
|
+
// THE FAILURE IS THE EXTERNAL-APP ACCENT, NOT THE EMBED. A fixed near-white is
|
|
483
|
+
// right for the platform violet (5.25:1) and for the 2.0 `--accent-hi`
|
|
484
|
+
// (8.33:1), and catastrophic for an External App that remaps `--accent` to a
|
|
485
|
+
// light brand colour: white on a pale gold #F5C542 measures 1.58:1, on a cyan
|
|
486
|
+
// #22D3EE 1.76:1. Three tokens are deliberately absent from this file precisely
|
|
487
|
+
// because the accent is the consuming app's to choose (see the header), so this
|
|
488
|
+
// is not a hypothetical — it is what the seam is for. The label has to follow
|
|
489
|
+
// the plate's LUMINANCE, which means the plate is what it is computed from.
|
|
490
|
+
|
|
491
|
+
// WHAT S1.11 RECORDED HERE WAS WRONG, and the correction is the reason this is
|
|
492
|
+
// a derivation rather than the one-line repoint that note proposed. The claim
|
|
493
|
+
// was that `app-surface-on-glass` pinning `--ink-inverse` to a dark literal
|
|
494
|
+
// dragged `--on-accent-fill` (then an alias of it) down to ~2.5:1 on #9333EA.
|
|
495
|
+
// It never did. A custom property is substituted at the element that DECLARES
|
|
496
|
+
// it: `--on-accent-fill: var(--ink-inverse)` resolves at `:root`, and the
|
|
497
|
+
// resolved value is what inherits. Every shipped `app-surface-on-glass` call
|
|
498
|
+
// site is a component host far below `:root`, so it re-values `--ink-inverse`
|
|
499
|
+
// for its own subtree and cannot reach an `--on-accent-fill` that was computed
|
|
500
|
+
// above it. Measured in Chromium against the real compiled mixin: inside the
|
|
501
|
+
// mixin `--ink-inverse` is `oklch(18% 0.005 250deg)` while `--on-accent-fill`
|
|
502
|
+
// is still `oklch(99% 0 0deg)`, rendering 5.25:1 — identical to outside it.
|
|
503
|
+
// The same rule is why there is no generic `--on-plate` token; see below.
|
|
504
|
+
|
|
505
|
+
// WHY A LIVE `var()` CHAIN AND NOT A BUILD-TIME COMPUTATION. Sass cannot see
|
|
506
|
+
// the input. `--accent` is a runtime knob: `FlyThemeService.applyAccent` writes
|
|
507
|
+
// it as an INLINE style on `<html>` from tenant/user branding, and
|
|
508
|
+
// `isAccentColor` admits anything `CSS.supports('color', …)` does. Any value
|
|
509
|
+
// resolved at compile time is answering a question about a colour that does not
|
|
510
|
+
// exist yet. Same reason `--shadow-accent` is a `color-mix` and not a hex.
|
|
511
|
+
|
|
512
|
+
// MECHANISMS REJECTED.
|
|
513
|
+
// · `contrast-color(var(--accent-fill))` is CSS Color 5 and is literally this
|
|
514
|
+
// feature, but it is not yet carried widely enough to be the only rung — and
|
|
515
|
+
// it splits black/white on the same WCAG luminance the expression below
|
|
516
|
+
// computes, so it would buy a dependency rather than an answer.
|
|
517
|
+
// · `light-dark()` pairs on the THEME. An accent's lightness is independent of
|
|
518
|
+
// the theme (an app may set a pale accent in dark mode), so it answers a
|
|
519
|
+
// different question and fails on exactly the case that motivates this.
|
|
520
|
+
// · `color-mix(… var(--accent) N%, white)` scales lightness monotonically; it
|
|
521
|
+
// cannot invert polarity, so it cannot cross the light/dark accent boundary.
|
|
522
|
+
// · "External Apps override the token" moves the defect into every integrator,
|
|
523
|
+
// silently — nothing can gate an app that forgets. The token stays
|
|
524
|
+
// overridable as an escape hatch; it is not the contract.
|
|
525
|
+
|
|
526
|
+
// THE EXPRESSION. Relative colour syntax in `srgb-linear` exposes `r`/`g`/`b`
|
|
527
|
+
// as exactly the linearised channels WCAG's relative-luminance formula takes,
|
|
528
|
+
// so `0.2126r + 0.7152g + 0.0722b` IS that luminance — no approximation. An
|
|
529
|
+
// oklch-`l` split is the tempting one-channel version and it is not safe: `l`
|
|
530
|
+
// and WCAG luminance disagree by up to ~0.04 on saturated hues, which near the
|
|
531
|
+
// boundary picks the wrong pole and ships a ~3.7:1 label. `(0.1791 - Y) *
|
|
532
|
+
// infinity` clamped to 0..1 is a step function: 1 (white) below the threshold,
|
|
533
|
+
// 0 (black) above. 0.1791 is where white and black contrast a background
|
|
534
|
+
// EQUALLY, so the worst case over the entire sRGB cube is 4.58:1 — the AA floor
|
|
535
|
+
// here is arithmetic, not a review. The explicit `/ 100%` is load-bearing:
|
|
536
|
+
// relative colour syntax inherits the ORIGIN's alpha when the slot is omitted,
|
|
537
|
+
// and this file's header requires a counter-ink to be OPAQUE or it composites
|
|
538
|
+
// into its own fill. A tenant accent may legitimately carry one — `isAccentColor`
|
|
539
|
+
// admits any value `CSS.supports('color', …)` accepts.
|
|
540
|
+
|
|
541
|
+
// TWO RUNGS. Relative colour syntax is Baseline since Firefox 128 (2024-07);
|
|
542
|
+
// anything older keeps the `--on-accent` white declared above, i.e. today's
|
|
543
|
+
// platform-correct behaviour. `@supports` is what makes that a fallback at all:
|
|
544
|
+
// a custom property accepts almost any token sequence at PARSE time, so the
|
|
545
|
+
// usual declare-it-twice ladder does not work here — the later declaration
|
|
546
|
+
// always wins and the failure surfaces at the consumer as an unset `color`.
|
|
547
|
+
// The `infinity` keyword needs no rung of its own; every engine with relative
|
|
548
|
+
// colour syntax predates its own support for it.
|
|
549
|
+
|
|
550
|
+
// THERE IS NO GENERIC `--on-plate` TOKEN, and the substitution rule above is
|
|
551
|
+
// why. A plate that is not the accent — `-board`'s presence chips carry a
|
|
552
|
+
// per-user colour from `presenceColorFor`, bound inline per element — cannot be
|
|
553
|
+
// served by a `:root` token, because a `:root` declaration substitutes `:root`'s
|
|
554
|
+
// input, not the chip's. Tried and measured: with an `--on-plate` token declared
|
|
555
|
+
// here and its input set on the chip, every chip resolved to the `:root`
|
|
556
|
+
// fallback and nothing changed. A plate the DS cannot know has to be derived in the
|
|
557
|
+
// CONSUMING declaration, on the element that paints it, which is what
|
|
558
|
+
// `canvas-diagram` / `canvas-fishbone` now do. `$_plate-ink` is the one
|
|
559
|
+
// statement of the formula; `plate-ink.spec.ts` holds those copies to it, and
|
|
560
|
+
// evaluates this one at both accent poles rather than pattern-matching it.
|
|
561
|
+
|
|
562
|
+
// Both theme classes are listed rather than leaning on `:root` alone: a future
|
|
563
|
+
// redeclaration of `--on-accent-fill` inside `html.dark-theme` (0,1,1) would
|
|
564
|
+
// otherwise silently outrank a bare `:root` (0,1,0) override.
|
|
565
|
+
|
|
566
|
+
// A Sass-private string, interpolated once per channel: all three take the SAME
|
|
567
|
+
// switch, and written inline Sass would try to evaluate `0.2126 * r` as
|
|
568
|
+
// arithmetic and fail. So this is both the escape hatch and the single place
|
|
569
|
+
// the formula exists — it cannot drift between the channels or the tokens.
|
|
570
|
+
$_plate-ink: 'clamp(0, (0.1791 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * infinity, 1)';
|
|
571
|
+
|
|
572
|
+
// The probe colour is `rgb(0 0 0)`, not a hex, so the identical prelude can be
|
|
573
|
+
// copied verbatim into `design-system-board` — whose own gate bans bare colour
|
|
574
|
+
// literals anywhere in a component stylesheet, prelude and comments included.
|
|
575
|
+
@supports (color: color(from rgb(0 0 0) srgb-linear r g b)) {
|
|
576
|
+
:root,
|
|
577
|
+
html.light-theme,
|
|
578
|
+
html.dark-theme {
|
|
579
|
+
--on-accent-fill: color(from var(--accent-fill) srgb-linear #{$_plate-ink} #{$_plate-ink} #{$_plate-ink} / 100%);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
@@ -27,13 +27,18 @@
|
|
|
27
27
|
// plate ~47px wide) cut every tool's tip off inside its own column, and no z-index could
|
|
28
28
|
// help — clipping is not a stacking question, and `position: fixed` on the pseudo would
|
|
29
29
|
// still be trapped by those plates' `backdrop-filter` containing block.
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
// `FlyTooltipDirective` now owns the surface for BOTH `[flyTooltip]` and `[data-tooltip]`:
|
|
32
32
|
// a `position: fixed` node parented to `<body>`, so it escapes every clip, flips placement
|
|
33
33
|
// and clamps to the viewport. Components that write `data-tooltip` imperatively (the DS
|
|
34
34
|
// icon button) list the directive in `hostDirectives`; templates with a static or bound
|
|
35
35
|
// `data-tooltip` must IMPORT `FlyTooltipDirective` for the label to appear.
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
// The surface itself (Nova's `--mat-tip-a`/`-b` material, opaque, no blur) lives entirely
|
|
38
|
+
// in the directive's injected `<style>` — see `fly-tooltip.directive.ts` for the recipe and
|
|
39
|
+
// why it is deliberately NOT a `glass.popover()` thin-blur surface (S1.8). Nothing here
|
|
40
|
+
// draws a tooltip; this selector only keeps the positioning context callers relied on.
|
|
41
|
+
|
|
37
42
|
// Only the positioning context the attribute implied is kept, since callers may still hang
|
|
38
43
|
// their own absolutely-positioned adornments (badges, dots) off a tooltip'd control.
|
|
39
44
|
[data-tooltip] {
|
|
@@ -1,9 +1,32 @@
|
|
|
1
|
-
@use '
|
|
1
|
+
@use 'fos-button-mixins' as *;
|
|
2
2
|
|
|
3
|
-
// ───
|
|
3
|
+
// ─── FlyOS Button Utility Classes (shared with the desktop shell) ─────────────
|
|
4
4
|
// Requires @use 'fly-theme' (or equivalent tokens) before this partial.
|
|
5
5
|
|
|
6
|
+
// ## The `.vos-` aliases are a deprecation window, not a style
|
|
7
|
+
|
|
8
|
+
// These three classes are the only PUBLISHED part of the old `vos-` prefix: External
|
|
9
|
+
// Apps write them in their own templates (measured: Circles authors `vos-btn` x21 and
|
|
10
|
+
// `vos-link` x4 across 6 files; Thoughts and PPM use none). Removing the old spelling
|
|
11
|
+
// is therefore a breaking change to this package's CSS surface — and a silent one,
|
|
12
|
+
// because a class that stops matching is not a build error in either repo, it simply
|
|
13
|
+
// renders unstyled.
|
|
14
|
+
|
|
15
|
+
// **And no gate would have caught it.** ds-compat extracts four things — the TS export
|
|
16
|
+
// surface, CSS CUSTOM PROPERTIES, CSS layers, and peer floors. It has no class-name
|
|
17
|
+
// extractor, so a renamed class is invisible to it: dropping `.vos-btn` outright still
|
|
18
|
+
// reports "federation-safe". The gate protects the token surface, not the class surface,
|
|
19
|
+
// even though the class surface is the one External-App TEMPLATES are written against.
|
|
20
|
+
|
|
21
|
+
// So `fos-` is canonical and `vos-` rides along as an alias, and the alias is doing the
|
|
22
|
+
// work the gate cannot.
|
|
23
|
+
|
|
24
|
+
// **Delete the `.vos-` halves at DS 2.0 (plan item S6.1b) — and only AFTER S6.2 lands
|
|
25
|
+
// the Circles migration.** Removing them first breaks Circles with no diagnostic.
|
|
26
|
+
// Verify against a grep of the Circles worktree, not of this repo.
|
|
27
|
+
|
|
6
28
|
@layer designsystem {
|
|
29
|
+
.fos-btn,
|
|
7
30
|
.vos-btn {
|
|
8
31
|
position: relative;
|
|
9
32
|
display: inline-flex;
|
|
@@ -60,11 +83,11 @@
|
|
|
60
83
|
}
|
|
61
84
|
|
|
62
85
|
&.platter::before {
|
|
63
|
-
@include
|
|
86
|
+
@include fos-platter-layers(inherit);
|
|
64
87
|
}
|
|
65
88
|
|
|
66
89
|
&:hover:not(:disabled, .selected, .active)::before {
|
|
67
|
-
@include
|
|
90
|
+
@include fos-platter-layers(inherit);
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
&.selected,
|
|
@@ -81,7 +104,7 @@
|
|
|
81
104
|
pointer-events: none;
|
|
82
105
|
|
|
83
106
|
&::before {
|
|
84
|
-
@include
|
|
107
|
+
@include fos-disabled-layers(inherit);
|
|
85
108
|
}
|
|
86
109
|
}
|
|
87
110
|
|
|
@@ -104,6 +127,8 @@
|
|
|
104
127
|
}
|
|
105
128
|
}
|
|
106
129
|
|
|
130
|
+
// `.vos-btn-symbol` alias: see the deprecation note at the top of this file.
|
|
131
|
+
.fos-btn-symbol,
|
|
107
132
|
.vos-btn-symbol {
|
|
108
133
|
position: relative;
|
|
109
134
|
display: inline-flex;
|
|
@@ -152,11 +177,11 @@
|
|
|
152
177
|
}
|
|
153
178
|
|
|
154
179
|
&.platter::before {
|
|
155
|
-
@include
|
|
180
|
+
@include fos-platter-layers(500px);
|
|
156
181
|
}
|
|
157
182
|
|
|
158
183
|
&:hover:not(:disabled, .selected, .active)::before {
|
|
159
|
-
@include
|
|
184
|
+
@include fos-platter-layers(500px);
|
|
160
185
|
}
|
|
161
186
|
|
|
162
187
|
&.selected,
|
|
@@ -173,7 +198,7 @@
|
|
|
173
198
|
pointer-events: none;
|
|
174
199
|
|
|
175
200
|
&::before {
|
|
176
|
-
@include
|
|
201
|
+
@include fos-disabled-layers(500px);
|
|
177
202
|
}
|
|
178
203
|
}
|
|
179
204
|
|
|
@@ -185,6 +210,8 @@
|
|
|
185
210
|
}
|
|
186
211
|
}
|
|
187
212
|
|
|
213
|
+
// `.vos-link` alias: see the deprecation note at the top of this file.
|
|
214
|
+
.fos-link,
|
|
188
215
|
.vos-link {
|
|
189
216
|
display: inline-flex;
|
|
190
217
|
align-items: center;
|
package/scss/_fly-theme.scss
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
@use 'theme-light';
|
|
7
7
|
@use 'theme-dark';
|
|
8
8
|
|
|
9
|
+
// ── Nova (UX v2) design language ─────────────────────────────────────────────
|
|
10
|
+
// Additive layer: new vocabulary only, nothing here re-values a token the three
|
|
11
|
+
// partials above already define (the colliding glass quartet is renamed
|
|
12
|
+
// `--glass2-*` per the D1 ruling). Canon: `skills/desktop-design-language.md`.
|
|
13
|
+
// `nova-glass` is mixins-only and is @used by components, not from here.
|
|
14
|
+
@use 'nova-tokens';
|
|
15
|
+
@use 'nova-motion';
|
|
16
|
+
@use 'nova-type';
|
|
17
|
+
|
|
9
18
|
// The inherited `color` for themed documents. Emits a rule, not tokens, so it
|
|
10
19
|
// lives apart from the theme partials — see its header for why the absence of a
|
|
11
20
|
// baseline was the root cause of the recurring black-ink-on-glass bug.
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Kept separate from desktop-app glass mixins so Business Apps only pull button-related SCSS.
|
|
3
|
-
|
|
4
|
-
@mixin
|
|
5
|
-
content: '';
|
|
6
|
-
position: absolute;
|
|
7
|
-
inset: 0;
|
|
8
|
-
border-radius: $radius;
|
|
9
|
-
pointer-events: none;
|
|
10
|
-
background:
|
|
11
|
-
linear-gradient(var(--btn-platter-lighten), var(--btn-platter-lighten)),
|
|
12
|
-
linear-gradient(var(--btn-platter-dodge), var(--btn-platter-dodge));
|
|
13
|
-
background-blend-mode: lighten, color-dodge;
|
|
14
|
-
mix-blend-mode: screen;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
@mixin
|
|
18
|
-
content: '';
|
|
19
|
-
position: absolute;
|
|
20
|
-
inset: 0;
|
|
21
|
-
border-radius: $radius;
|
|
22
|
-
pointer-events: none;
|
|
23
|
-
background:
|
|
24
|
-
linear-gradient(var(--btn-disabled-lighten), var(--btn-disabled-lighten)),
|
|
25
|
-
linear-gradient(var(--btn-disabled-dodge), var(--btn-disabled-dodge));
|
|
26
|
-
background-blend-mode: lighten, color-dodge;
|
|
27
|
-
mix-blend-mode: screen;
|
|
28
|
-
}
|
|
1
|
+
// FlyOS button pseudo-element layers (used by _business-app-buttons.scss).
|
|
2
|
+
// Kept separate from desktop-app glass mixins so Business Apps only pull button-related SCSS.
|
|
3
|
+
|
|
4
|
+
@mixin fos-platter-layers($radius) {
|
|
5
|
+
content: '';
|
|
6
|
+
position: absolute;
|
|
7
|
+
inset: 0;
|
|
8
|
+
border-radius: $radius;
|
|
9
|
+
pointer-events: none;
|
|
10
|
+
background:
|
|
11
|
+
linear-gradient(var(--btn-platter-lighten), var(--btn-platter-lighten)),
|
|
12
|
+
linear-gradient(var(--btn-platter-dodge), var(--btn-platter-dodge));
|
|
13
|
+
background-blend-mode: lighten, color-dodge;
|
|
14
|
+
mix-blend-mode: screen;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@mixin fos-disabled-layers($radius) {
|
|
18
|
+
content: '';
|
|
19
|
+
position: absolute;
|
|
20
|
+
inset: 0;
|
|
21
|
+
border-radius: $radius;
|
|
22
|
+
pointer-events: none;
|
|
23
|
+
background:
|
|
24
|
+
linear-gradient(var(--btn-disabled-lighten), var(--btn-disabled-lighten)),
|
|
25
|
+
linear-gradient(var(--btn-disabled-dodge), var(--btn-disabled-dodge));
|
|
26
|
+
background-blend-mode: lighten, color-dodge;
|
|
27
|
+
mix-blend-mode: screen;
|
|
28
|
+
}
|
package/scss/_ink-baseline.scss
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
// ─── Inherited-ink baseline ──────────────────────────────────────────────────
|
|
2
|
-
|
|
3
|
-
// Establishes the document's inherited `color`. Until this existed, NOTHING in
|
|
4
|
-
// the shell ever set `color` on `html` or `body` — `_theme-light.scss` and
|
|
5
|
-
// `_theme-dark.scss` declare custom properties only. So the inherited colour was
|
|
6
|
-
// the user-agent default, **black**, and every component that says
|
|
7
|
-
// `color: inherit` (fly-tree-nav rows, fly-breadcrumb's current crumb,
|
|
8
|
-
// fly-search-input's typed text, fly-card's meta slot, and any consumer markup
|
|
9
|
-
// that simply doesn't mention colour) rendered black ink on the smoked glass.
|
|
10
|
-
|
|
11
|
-
// That is why the "text is invisible in light theme" bug kept coming back app
|
|
12
|
-
// after app: it was never really an app's mistake. There was no baseline to
|
|
13
|
-
// inherit, so *forgetting* to set a colour was the failure mode, and the fix
|
|
14
|
-
// kept being applied one component at a time. `--text-color` is white in BOTH
|
|
15
|
-
// themes since the
|
|
16
|
-
|
|
17
|
-
// Scoped to the two theme classes on purpose — NOT `:root`. A business app
|
|
18
|
-
// running STANDALONE renders on its own opaque paper (`--bg`, near-black
|
|
19
|
-
// `--ink`) and does not load `fly-theme` at all; keying on the class means a
|
|
20
|
-
// surface that never opted into a shell theme is never repainted white.
|
|
21
|
-
|
|
22
|
-
// A deliberate paper surface inside the shell (the documents editor) still
|
|
23
|
-
// brings its own dark ink locally — a component rule beats this baseline, which
|
|
24
|
-
// is exactly the intended precedence.
|
|
25
|
-
|
|
26
|
-
html.light-theme,
|
|
27
|
-
html.dark-theme {
|
|
28
|
-
color: var(--text-color);
|
|
29
|
-
}
|
|
1
|
+
// ─── Inherited-ink baseline ──────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
// Establishes the document's inherited `color`. Until this existed, NOTHING in
|
|
4
|
+
// the shell ever set `color` on `html` or `body` — `_theme-light.scss` and
|
|
5
|
+
// `_theme-dark.scss` declare custom properties only. So the inherited colour was
|
|
6
|
+
// the user-agent default, **black**, and every component that says
|
|
7
|
+
// `color: inherit` (fly-tree-nav rows, fly-breadcrumb's current crumb,
|
|
8
|
+
// fly-search-input's typed text, fly-card's meta slot, and any consumer markup
|
|
9
|
+
// that simply doesn't mention colour) rendered black ink on the smoked glass.
|
|
10
|
+
|
|
11
|
+
// That is why the "text is invisible in light theme" bug kept coming back app
|
|
12
|
+
// after app: it was never really an app's mistake. There was no baseline to
|
|
13
|
+
// inherit, so *forgetting* to set a colour was the failure mode, and the fix
|
|
14
|
+
// kept being applied one component at a time. `--text-color` is white in BOTH
|
|
15
|
+
// themes since the vibrancy flip, so one declaration settles it everywhere.
|
|
16
|
+
|
|
17
|
+
// Scoped to the two theme classes on purpose — NOT `:root`. A business app
|
|
18
|
+
// running STANDALONE renders on its own opaque paper (`--bg`, near-black
|
|
19
|
+
// `--ink`) and does not load `fly-theme` at all; keying on the class means a
|
|
20
|
+
// surface that never opted into a shell theme is never repainted white.
|
|
21
|
+
|
|
22
|
+
// A deliberate paper surface inside the shell (the documents editor) still
|
|
23
|
+
// brings its own dark ink locally — a component rule beats this baseline, which
|
|
24
|
+
// is exactly the intended precedence.
|
|
25
|
+
|
|
26
|
+
html.light-theme,
|
|
27
|
+
html.dark-theme {
|
|
28
|
+
color: var(--text-color);
|
|
29
|
+
}
|