@flyos/design-system 0.0.1-reserved → 1.1.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/package.json CHANGED
@@ -1,7 +1,56 @@
1
1
  {
2
2
  "name": "@flyos/design-system",
3
- "version": "0.0.1-reserved",
4
- "description": "FlyOS design system — scope reservation placeholder.",
3
+ "version": "1.1.0",
4
+ "description": "FlyOS design system — shared components, directives, pipes, services, and models for Business App developers.",
5
+ "keywords": [
6
+ "flyos",
7
+ "design-system",
8
+ "angular"
9
+ ],
5
10
  "license": "MIT",
6
- "publishConfig": { "access": "public" }
7
- }
11
+ "exports": {
12
+ "./federation/with-fly-federation": {
13
+ "require": "./federation/with-fly-federation.cjs",
14
+ "default": "./federation/with-fly-federation.cjs"
15
+ },
16
+ "./federation/*": "./federation/*",
17
+ "./package.json": {
18
+ "default": "./package.json"
19
+ },
20
+ ".": {
21
+ "types": "./types/flyos-design-system.d.ts",
22
+ "default": "./fesm2022/flyos-design-system.mjs"
23
+ }
24
+ },
25
+ "dependencies": {
26
+ "cropperjs": "^1.6.2",
27
+ "tslib": "^2.3.0"
28
+ },
29
+ "peerDependencies": {
30
+ "@flyos/design-system-emoji-pack": "*",
31
+ "@angular/animations": "^21.0.0",
32
+ "@angular/cdk": "^21.0.0",
33
+ "@angular/common": "^21.0.0",
34
+ "@angular/core": "^21.0.0",
35
+ "@angular/forms": "^21.0.0",
36
+ "@angular/router": "^21.0.0",
37
+ "@microsoft/signalr": "^10.0.0",
38
+ "primeicons": "^7.0.0",
39
+ "rxjs": "~7.8.0"
40
+ },
41
+ "peerDependenciesMeta": {
42
+ "@microsoft/signalr": {
43
+ "optional": true
44
+ },
45
+ "@flyos/design-system-emoji-pack": {
46
+ "optional": true
47
+ }
48
+ },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "module": "fesm2022/flyos-design-system.mjs",
53
+ "typings": "types/flyos-design-system.d.ts",
54
+ "sideEffects": false,
55
+ "type": "module"
56
+ }
@@ -0,0 +1,366 @@
1
+ // ─── App-surface tokens — the BUSINESS-APP content layer ─────────────────────
2
+ // WHY THIS IS A SEPARATE FAMILY FROM `--label-*` / `--surface-*`
3
+ // The DS carries two token families because it dresses two genuinely different
4
+ // surfaces, not because anyone forgot to merge them:
5
+
6
+ // 1. `--label-*` / `--surface-*` / `--window-*` / `--glass-*` (_theme-light/dark)
7
+ // SHELL CHROME. Translucent glass composited over the user's wallpaper.
8
+ // `--surface-card` is a 20%-white LIFT on the window plate — it is only ever
9
+ // meaningful stacked on that plate.
10
+
11
+ // 2. `--ink-*` / `--bg-*` / `--line-*` (this file)
12
+ // BUSINESS-APP CONTENT. An app's own opaque reading surface: white cards on
13
+ // a near-white page in light, dark cards on a darker page in dark. Apps that
14
+ // also embed in the shell re-tint THESE to glass in their own
15
+ // `.fly-shell <app>` block (see Circles' `circles-theme.scss`) — that
16
+ // override layer is the seam, and it only works because the resting values
17
+ // are opaque.
18
+
19
+ // Pointing `--bg-2` at `--surface-card` was evaluated and rejected: standalone,
20
+ // a 20%-white card over a 98.5%-white page stops reading as a card at all. The
21
+ // two families stay distinct on purpose; this comment is the contract.
22
+
23
+ // ADDITIVE ONLY. `tools/ds-compat` classifies re-valuing an existing theme
24
+ // token as MAJOR (it forks the Native Federation shared singleton for every
25
+ // remote), so nothing here may redefine a token another DS partial owns.
26
+ //
27
+ // Three tokens are deliberately ABSENT — they are the consuming app's BRAND and
28
+ // every app already sets them in its own theme file:
29
+ // --accent --accent-soft --focus-ring
30
+ //
31
+ // The other nine platform tokens (--danger* --warning* --purple --scrim
32
+ // --glass-blur) USED to be absent too, on the assumption that the platform layer
33
+ // supplies them. That assumption only holds when an app runs FEDERATED, where
34
+ // the desktop shell emits `_theme-light/dark.scss`. A business app running
35
+ // STANDALONE loads only this file plus its own theme — `@use 'fly-theme'` is
36
+ // commented out in both Circles and Thoughts — so `var(--danger)` resolved to
37
+ // nothing and error text rendered colourless. Confirmed in a live browser, not
38
+ // inferred: 9 of the 12 computed to the empty string on Thoughts standalone.
39
+ //
40
+ // They are now mirrored below at the SAME values `_theme-light/dark.scss` uses.
41
+ // Mirroring (rather than picking new values) is what makes this safe under the
42
+ // cascade: this file's `:root, html.light-theme` selector can tie with the
43
+ // shell's `html.light-theme`, and when the values are identical it does not
44
+ // matter which one wins.
45
+
46
+ // Consumed by the `fly-*` component library in `lib/components/`. Emitted via
47
+ // `_fly-theme.scss`, so `@use 'fly-theme'` is all a consumer needs.
48
+ // ─────────────────────────────────────────────────────────────────────────────
49
+
50
+ :root,
51
+ html.light-theme {
52
+ // ── Neutrals — text ──────────────────────────────────────────────────────
53
+ --ink: oklch(18% 0.005 250deg); // primary text, primary button fills
54
+ --ink-2: oklch(32% 0.005 250deg); // secondary text
55
+ --ink-3: oklch(50% 0.005 250deg); // tertiary text, icons
56
+ --ink-4: oklch(65% 0.005 250deg); // disabled, muted labels
57
+ --ink-inverse: oklch(99% 0 0deg); // text on dark / gradient fills
58
+ --ink-hover: oklch(28% 0.005 250deg); // hover shade of --ink-filled controls
59
+
60
+ // Label for a fill of `--ink` itself (the primary button). Distinct from
61
+ // `--ink-inverse`, which is a FIXED near-white for coloured/gradient fills and
62
+ // therefore wrong here: `--ink` flips per theme, so its label must flip too.
63
+ //
64
+ // CONTRACT — anything that redefines `--ink` MUST redefine `--on-ink`.
65
+ // It must also be OPAQUE. The shell-embed glass remaps in Circles and Thoughts
66
+ // set `--ink` to white and every surface token to a translucent veil; a
67
+ // translucent label composites into the fill and vanishes.
68
+ --on-ink: var(--ink-inverse);
69
+
70
+ // ── Neutrals — borders ───────────────────────────────────────────────────
71
+ --line: oklch(92% 0.004 250deg); // default borders
72
+ --line-2: oklch(88% 0.004 250deg); // hover borders
73
+ --line-3: oklch(96% 0.003 250deg); // inner row dividers
74
+
75
+ // ── Surfaces (opaque by design — see the header note) ────────────────────
76
+ --bg: oklch(98.5% 0.003 100deg); // app background (warm off-white)
77
+ --bg-2: oklch(100% 0 0deg); // cards, table
78
+ --bg-3: oklch(97% 0.004 100deg); // sidebar, table header
79
+ --bg-hover: oklch(96% 0.005 250deg);
80
+
81
+ // ── Brand primitives ─────────────────────────────────────────────────────
82
+ // `--purple` / `--accent` / `--accent-soft` are platform-owned (_theme-*.scss).
83
+ --teal: oklch(74% 0.13 195deg);
84
+ --grad: linear-gradient(
85
+ 135deg,
86
+ oklch(82% 0.12 325deg),
87
+ oklch(60% 0.19 305deg),
88
+ oklch(45% 0.2 285deg)
89
+ );
90
+ --grad-soft: linear-gradient(
91
+ 135deg,
92
+ oklch(74% 0.13 195deg / 12%),
93
+ oklch(55% 0.18 295deg / 12%)
94
+ );
95
+
96
+ // ── Accent plate/ink PAIRS ───────────────────────────────────────────────
97
+ // NEVER paint --accent on an --accent-soft plate: that is purple-on-purple,
98
+ // and it is the bug that keeps coming back. Two plates, two inks:
99
+
100
+ // 1. --accent-soft is a TINT the consuming context may redefine (the shell
101
+ // resolves it to `color-mix(… 18%, transparent)`, i.e. a translucent wash
102
+ // over unknowable glass). The only ink correct against it in every mode
103
+ // is the ambient --ink, which each theme already contrasts with its own
104
+ // background.
105
+ --on-accent-soft: var(--ink);
106
+
107
+ // 2. --accent-fill is a SOLID plate we own outright, so its ink is knowable
108
+ // regardless of backdrop — always white. Badges/chips use this pair, so
109
+ // their contrast cannot be broken by a host token override.
110
+ --accent-fill: var(--accent);
111
+
112
+ // Aliased, not a second copy of the same white: "ink on a solid accent plate"
113
+ // and "ink on a dark/gradient fill" are the same requirement, so they must
114
+ // move together if the inverse ink is ever re-tuned.
115
+ --on-accent-fill: var(--ink-inverse);
116
+
117
+ // ── Interaction state layers ─────────────────────────────────────────────
118
+ // Translucent veils layered OVER a surface for hover/press/selected instead
119
+ // of swapping its bg colour — one value reads correctly on any background
120
+ // (the visionOS / Material "state layer" model), so hover looks consistent
121
+ // across cards, rows, chips and glass plates alike.
122
+ --state-hover: oklch(50% 0.03 285deg / 5%);
123
+ --state-press: oklch(50% 0.03 285deg / 9%);
124
+ --state-selected: var(--accent-soft);
125
+
126
+ // Bright top rim that gives a translucent plate physical depth. Inert on
127
+ // opaque surfaces; the shell's glass mode leans on it.
128
+ --edge-highlight: oklch(100% 0 0deg / 50%);
129
+
130
+ // ── Icon size ramp ───────────────────────────────────────────────────────
131
+ // ONE scale for every icon (PrimeIcons + inline SVG). Icons had drifted to
132
+ // ~13 different sizes, which is why they never lined up beside each other.
133
+ --icon-xs: 12px; // dense meta rows, chips
134
+ --icon-sm: 14px; // text buttons, section-header actions
135
+ --icon-md: 16px; // default — icon buttons, rails, nav
136
+ --icon-lg: 20px; // emphasis / empty states
137
+
138
+ // ── Status chips (workflow-generic) ──────────────────────────────────────
139
+ --st-published: oklch(55% 0.13 165deg);
140
+ --st-published-bg: oklch(96% 0.04 165deg);
141
+ --st-approved: oklch(55% 0.15 245deg);
142
+ --st-approved-bg: oklch(96% 0.03 245deg);
143
+ --st-draft: oklch(55% 0.005 250deg);
144
+ --st-draft-bg: oklch(95% 0.003 250deg);
145
+ --st-archived: oklch(55% 0.06 60deg);
146
+ --st-archived-bg: oklch(96% 0.025 80deg);
147
+
148
+ // ── Semantic feedback ────────────────────────────────────────────────────
149
+ // Success has no platform equivalent (`--status-success` is a raw dot colour,
150
+ // not a fg/bg pair), so it ships here, aliased onto the published status chip.
151
+ --success: var(--st-published);
152
+ --success-bg: var(--st-published-bg);
153
+
154
+ // Standalone fallbacks for the platform tokens — see the header. Values are
155
+ // copied verbatim from `_theme-light.scss`; keep them in sync if that file
156
+ // changes, or a standalone app drifts from its federated self.
157
+ --danger: #ff6b6b;
158
+ --danger-bg: rgb(255 59 48 / 20%);
159
+ --danger-line: rgb(255 59 48 / 40%);
160
+ --warning: #febc2e;
161
+ --warning-bg: rgb(254 188 46 / 18%);
162
+ --warning-fg: rgb(255 255 255 / 96%);
163
+ --purple: #9333ea;
164
+ --scrim: rgb(0 0 0 / 50%);
165
+ --glass-blur: 24px;
166
+
167
+ // ── Spacing scale (4px base) ─────────────────────────────────────────────
168
+ --sp-1: 4px;
169
+ --sp-2: 8px;
170
+ --sp-3: 12px;
171
+ --sp-4: 16px;
172
+ --sp-5: 22px;
173
+ --sp-6: 28px;
174
+ --sp-7: 40px;
175
+ --sp-8: 56px;
176
+
177
+ // ── Typography scale ─────────────────────────────────────────────────────
178
+ --text-2xs: 11.5px; // field labels, field errors, footer hints
179
+ --text-xs: 11px; // eyebrows, badges, metric labels
180
+ --text-sm: 12.5px; // meta, hints, crumbs
181
+ --text-md: 13.5px; // body in cards, subtitles
182
+ --text-base: 14px; // controls, table body
183
+ --text-lg: 16px; // page body
184
+ --text-xl: 18px; // card titles, dialog titles
185
+ --text-2xl: 28px; // detail page h1
186
+ --text-3xl: 32px; // listing page h1
187
+ --text-display: 44px; // hero title
188
+ --fw-book: 450; // between regular and medium (segmented buttons)
189
+ --fw-medium: 500;
190
+ --fw-semibold: 600;
191
+ --fw-bold: 700;
192
+ --tracking-tight: -0.02em;
193
+ --tracking-wide: 0.06em;
194
+
195
+ // Type families. `--font-family` / `--font-family-mono` are the platform's
196
+ // names; these are the short aliases the component library reads.
197
+ --font-sans: var(--font-family);
198
+
199
+ // Monospace is retired — the UI is one sans face throughout. Kept (aliased)
200
+ // rather than deleted so existing `var(--font-mono)` consumers keep
201
+ // resolving; the `.mono` utility still contributes tabular-nums, which is
202
+ // what those id/number treatments actually need.
203
+ --font-mono: var(--font-sans);
204
+
205
+ // ── Radii ────────────────────────────────────────────────────────────────
206
+ --r-sm: 6px; // icon buttons, kbd
207
+ --r-md: 8px; // buttons, inputs, nav items
208
+ --r-lg: 12px; // cards, table wrapper, filter panel
209
+ --r-xl: 16px; // reserved
210
+
211
+ // ── Layout ───────────────────────────────────────────────────────────────
212
+ --row-h: 56px; // table row
213
+
214
+ // ── Z-index scale ────────────────────────────────────────────────────────
215
+ --z-sticky: 10;
216
+ --z-bulkbar: 50;
217
+ --z-overlay: 100; // drawers, scrims
218
+ --z-dialog: 110; // centered dialogs / confirms
219
+ --z-toast: 200;
220
+ --z-tooltip: 1000;
221
+
222
+ // ── Shadows ──────────────────────────────────────────────────────────────
223
+ --shadow-card: 0 1px 2px oklch(0% 0 0deg / 4%);
224
+ --shadow-active: 0 1px 2px oklch(0% 0 0deg / 4%), 0 0 0 1px var(--line);
225
+ --shadow-bulk: 0 6px 20px oklch(0% 0 0deg / 10%);
226
+ --shadow-filter: 0 12px 36px oklch(0% 0 0deg / 6%);
227
+ --shadow-panel: var(--shadow-filter), 0 1px 0 oklch(100% 0 0deg) inset;
228
+ --shadow-raised: 0 1px 2px oklch(0% 0 0deg / 8%);
229
+
230
+ // DERIVED off --accent, never a copy of its hex: a hardcoded purple here
231
+ // would not follow a runtime accent override (FlyThemeService.applyAccent),
232
+ // so you would get the new accent with the old glow beside it.
233
+ --shadow-accent: 0 4px 12px color-mix(in srgb, var(--accent) 25%, transparent);
234
+ --shadow-tooltip: 0 4px 16px oklch(0% 0 0deg / 18%);
235
+ --shadow-focus-soft: 0 0 0 3px oklch(0% 0 0deg / 4%);
236
+ --shadow-drawer: -24px 0 64px oklch(0% 0 0deg / 8%); // end-side panel (LTR)
237
+ --shadow-drawer-rtl: 24px 0 64px oklch(0% 0 0deg / 8%); // mirrored under RTL
238
+ --shadow-dialog: 0 24px 64px oklch(18% 0.005 250deg / 18%), 0 8px 24px oklch(18% 0.005 250deg / 10%);
239
+ --shadow-toast: 0 12px 32px oklch(18% 0.005 250deg / 14%), 0 4px 12px oklch(18% 0.005 250deg / 8%);
240
+
241
+ // ── Transitions ──────────────────────────────────────────────────────────
242
+ --t-state: 0.12s ease;
243
+ --t-layout: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
244
+ --t-overlay: 0.18s cubic-bezier(0.2, 0.7, 0.2, 1);
245
+ --t-drawer: 0.32s cubic-bezier(0.32, 0.72, 0.2, 1);
246
+ }
247
+
248
+ // ── Dark theme ───────────────────────────────────────────────────────────────
249
+ // Sizing, radii, type, spacing, z-index and transition tokens are theme-neutral
250
+ // and inherit the block above unchanged. Alias-based tokens (--success*,
251
+ // --on-accent-*, --state-selected, --font-*) re-resolve through their targets
252
+ // automatically and are NOT redeclared — a second declaration would just be a
253
+ // second place to get it wrong.
254
+ html.dark-theme {
255
+ // The nine platform fallbacks carry the SAME values in both themes upstream,
256
+ // except this one — `_theme-dark.scss` widens the blur. Redeclaring only the
257
+ // token that actually differs keeps the rest inheriting from the block above.
258
+ --glass-blur: 40px;
259
+
260
+ // ── Neutrals (inverted lightness) ────────────────────────────────────────
261
+ --ink: oklch(95% 0.005 250deg);
262
+ --ink-2: oklch(78% 0.005 250deg);
263
+ --ink-3: oklch(62% 0.005 250deg);
264
+ --ink-4: oklch(45% 0.005 250deg);
265
+ --ink-hover: oklch(85% 0.005 250deg);
266
+ // Flips with --ink above: the primary button is a near-white pill here, so its
267
+ // label goes near-black. Aliased to --bg (the darkest opaque surface in this
268
+ // theme) rather than repeating its literal — see the contract on the :root decl.
269
+ --on-ink: var(--bg);
270
+ --line: oklch(22% 0.004 250deg);
271
+ --line-2: oklch(28% 0.004 250deg);
272
+ --line-3: oklch(18% 0.003 250deg);
273
+ --bg: oklch(14% 0.003 250deg);
274
+ --bg-2: oklch(17% 0.003 250deg);
275
+ --bg-3: oklch(20% 0.004 250deg);
276
+ --bg-hover: oklch(24% 0.005 250deg);
277
+
278
+ // ── Brand — higher chroma/lightness to pop on dark ───────────────────────
279
+ --teal: oklch(78% 0.13 195deg);
280
+ --grad: linear-gradient(
281
+ 135deg,
282
+ oklch(85% 0.12 325deg),
283
+ oklch(70% 0.19 305deg),
284
+ oklch(60% 0.2 285deg)
285
+ );
286
+ --grad-soft: linear-gradient(
287
+ 135deg,
288
+ oklch(78% 0.13 195deg / 18%),
289
+ oklch(68% 0.18 295deg / 18%)
290
+ );
291
+
292
+ // ── Interaction state layers — light veil on dark surfaces ───────────────
293
+ --state-hover: oklch(95% 0.02 285deg / 6%);
294
+ --state-press: oklch(95% 0.02 285deg / 11%);
295
+ --edge-highlight: oklch(100% 0 0deg / 6%);
296
+
297
+ // ── Status chips — dim bg, bright fg ─────────────────────────────────────
298
+ --st-published: oklch(85% 0.13 165deg);
299
+ --st-published-bg: oklch(28% 0.06 165deg);
300
+ --st-approved: oklch(82% 0.15 245deg);
301
+ --st-approved-bg: oklch(28% 0.06 245deg);
302
+ --st-draft: var(--ink-2); // same grey as secondary text on dark
303
+ --st-draft-bg: oklch(24% 0.003 250deg);
304
+ --st-archived: oklch(82% 0.06 60deg);
305
+ --st-archived-bg: oklch(28% 0.05 60deg);
306
+
307
+ // ── Shadows — deeper opacity on dark surfaces ────────────────────────────
308
+ --shadow-card: 0 1px 2px oklch(0% 0 0deg / 35%);
309
+ --shadow-active: 0 1px 2px oklch(0% 0 0deg / 40%), 0 0 0 1px var(--line);
310
+ --shadow-bulk: 0 6px 20px oklch(0% 0 0deg / 55%);
311
+ --shadow-filter: 0 12px 36px oklch(0% 0 0deg / 50%);
312
+ --shadow-panel: var(--shadow-filter), 0 1px 0 oklch(100% 0 0deg / 5%) inset;
313
+ --shadow-accent: 0 4px 12px color-mix(in srgb, var(--accent) 30%, transparent);
314
+ --shadow-focus-soft: 0 0 0 3px oklch(100% 0 0deg / 6%);
315
+ --shadow-drawer: -24px 0 64px oklch(0% 0 0deg / 50%);
316
+ --shadow-drawer-rtl: 24px 0 64px oklch(0% 0 0deg / 50%);
317
+ --shadow-dialog: 0 24px 64px oklch(0% 0 0deg / 60%), 0 8px 24px oklch(0% 0 0deg / 40%);
318
+ --shadow-toast: 0 12px 32px oklch(0% 0 0deg / 50%), 0 4px 12px oklch(0% 0 0deg / 30%);
319
+ }
320
+
321
+ // ── Chrome-token bridge (STANDALONE ONLY) ────────────────────────────────────
322
+ // The older DS components (everything outside lib/app-surface) theme from the
323
+ // SHELL-CHROME family — --label-*, --surface-*, --text-color*, --separator* —
324
+ // and each reference carries a hardcoded literal fallback, e.g.
325
+ // `var(--label-primary, #1d1d1f)` or `var(--surface-card, #fff)`. 211 such
326
+ // fallbacks exist across the library.
327
+ //
328
+ // Those literals are theme-BLIND: a single constant picked by whoever wrote the
329
+ // component. Federated that never shows, because the desktop shell defines the
330
+ // whole chrome family. Standalone, no one does — so a business app got whatever
331
+ // constant each component happened to hardcode, in BOTH light and dark.
332
+ //
333
+ // Aliasing the chrome names onto the app-surface family fixes all 211 sites at
334
+ // once, without touching a single component: the var() resolves through the
335
+ // token and never reaches its literal. The targets are theme-aware, so these
336
+ // follow light/dark automatically.
337
+ //
338
+ // SELECTOR IS LOAD-BEARING: this block is `:root` ALONE — deliberately not
339
+ // `:root, html.light-theme` like the blocks above. The shell declares every one
340
+ // of these under `html.light-theme` / `html.dark-theme` (specificity 0,1,1),
341
+ // which beats a bare `:root` (0,1,0). So federated, the shell's chrome always
342
+ // wins and nothing about the desktop changes; standalone, nothing else declares
343
+ // them and these apply. Widening this selector would create a specificity TIE
344
+ // with the shell and hand the desktop's own chrome to source order.
345
+ :root {
346
+ // Text
347
+ --label-primary: var(--ink);
348
+ --label-secondary: var(--ink-3);
349
+ --label-tertiary: var(--ink-4);
350
+ --text-color: var(--ink);
351
+ --text-color-secondary: var(--ink-3);
352
+
353
+ // Surfaces
354
+ --surface-ground: var(--bg);
355
+ --surface-card: var(--bg-2);
356
+ --surface-section: var(--bg-3);
357
+ --surface-overlay: var(--bg-2);
358
+ --surface-overlay-strong: var(--bg-2);
359
+
360
+ // Lines + interaction states
361
+ --surface-border: var(--line);
362
+ --separator: var(--line);
363
+ --separator-primary: var(--line);
364
+ --surface-hover: var(--bg-hover);
365
+ --surface-active: var(--state-selected);
366
+ }
@@ -0,0 +1,82 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // UI library global utilities (generic). EMITS CSS — import once from the
3
+ // global stylesheet, after _tokens.scss.
4
+ // ─────────────────────────────────────────────────────────────────────────────
5
+ @use 'shell-embed-bridge';
6
+
7
+ // Class form of the app-surface → shell-glass bridge, for consumers that cannot
8
+ // reach the mixin: a component with INLINE `styles:` (no SCSS pipeline), or a
9
+ // subtree portalled out of its app's stylesheet scope. Prefer the mixin in a
10
+ // real `.scss` file; this exists so the map is never retyped by hand. See
11
+ // `_shell-embed-bridge.scss` for why the bridge is needed at all.
12
+ .fly-app-surface-on-glass {
13
+ @include shell-embed-bridge.app-surface-on-glass;
14
+ }
15
+
16
+ // Tabular numerals / code — reusable in templates without restyling a component.
17
+ .mono {
18
+ font-family: var(--font-mono);
19
+ font-variant-numeric: tabular-nums;
20
+ }
21
+
22
+ // ── Tooltip (CSS-only, no JS dependency) ─────────────────────────────────────
23
+ // Add `data-tooltip="text"` to any element to show a floating label on hover or
24
+ // keyboard focus. Always pair with `aria-label` for screen-reader users —
25
+ // data-tooltip is a *visual* affordance only (the flyTooltip directive wires
26
+ // both together). Consumers should use `data-tooltip` *instead of* `title`.
27
+ [data-tooltip] {
28
+ position: relative;
29
+ }
30
+
31
+ [data-tooltip]::before,
32
+ [data-tooltip]::after {
33
+ position: absolute;
34
+ left: 50%;
35
+ pointer-events: none;
36
+ opacity: 0;
37
+ transition: opacity 0.14s ease, transform 0.14s ease;
38
+ z-index: var(--z-tooltip);
39
+ }
40
+
41
+ [data-tooltip]::before {
42
+ content: attr(data-tooltip);
43
+ bottom: calc(100% + 8px);
44
+ transform: translateX(-50%) translateY(3px);
45
+ background: var(--ink);
46
+ color: var(--ink-inverse);
47
+ font-family: var(--font-sans, system-ui, sans-serif);
48
+ font-size: var(--text-xs);
49
+ font-weight: var(--fw-medium);
50
+ line-height: 1.3;
51
+ letter-spacing: 0;
52
+ padding: 5px 9px;
53
+ border-radius: var(--r-sm);
54
+ white-space: nowrap;
55
+ box-shadow: var(--shadow-tooltip);
56
+ }
57
+
58
+ [data-tooltip]::after {
59
+ content: '';
60
+ bottom: calc(100% + 3px);
61
+ transform: translateX(-50%) translateY(3px);
62
+ width: 0;
63
+ height: 0;
64
+ border-left: 5px solid transparent;
65
+ border-right: 5px solid transparent;
66
+ border-top: 5px solid var(--ink);
67
+ }
68
+
69
+ [data-tooltip]:hover::before,
70
+ [data-tooltip]:hover::after,
71
+ [data-tooltip]:focus-visible::before,
72
+ [data-tooltip]:focus-visible::after {
73
+ opacity: 1;
74
+ transform: translateX(-50%) translateY(0);
75
+ }
76
+
77
+ // ── CDK overlay container ────────────────────────────────────────────────────
78
+ // Styled entirely by @angular/cdk/overlay-prebuilt.css (imported by the global
79
+ // stylesheet). Its default z-index 1000 is correct here: a connected dropdown
80
+ // (fly-action-menu) is the topmost transient and every library layer sits
81
+ // below 1000. Do NOT re-pin it — the container is shared with the federated
82
+ // shell, so an override leaks into the host's overlays.