@flyos/design-system 0.0.1-reserved → 1.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/federation/with-fly-federation.cjs +86 -0
- package/fesm2022/flyos-design-system.mjs +20534 -0
- package/fesm2022/flyos-design-system.mjs.map +1 -0
- package/package.json +53 -4
- package/scss/_app-surface-tokens.scss +366 -0
- package/scss/_app-surface-utilities.scss +72 -0
- package/scss/_business-app-buttons.scss +212 -0
- package/scss/_fly-theme.scss +27 -0
- package/scss/_ink-baseline.scss +29 -0
- package/scss/_menu-anchor.scss +22 -0
- package/scss/_native-select.scss +26 -0
- package/scss/_theme-auto.scss +6 -0
- package/scss/_theme-dark-vars.scss +56 -0
- package/scss/_theme-dark.scss +196 -0
- package/scss/_theme-light.scss +231 -0
- package/scss/_tokens.scss +119 -0
- package/scss/_vos-button-mixins.scss +28 -0
- package/types/flyos-design-system.d.ts +9837 -0
- package/types/flyos-design-system.d.ts.map +1 -0
- package/ui-fonts/geist-cyrillic.woff2 +0 -0
- package/ui-fonts/geist-latin-ext.woff2 +0 -0
- package/ui-fonts/geist-latin.woff2 +0 -0
- package/ui-fonts/geist-mono-cyrillic.woff2 +0 -0
- package/ui-fonts/geist-mono-latin-ext.woff2 +0 -0
- package/ui-fonts/geist-mono-latin.woff2 +0 -0
- package/ui-fonts/geist.css +61 -0
- package/ui-fonts/primeicons/fonts/primeicons.ttf +0 -0
- package/ui-fonts/primeicons/fonts/primeicons.woff +0 -0
- package/ui-fonts/primeicons/fonts/primeicons.woff2 +0 -0
- package/ui-fonts/primeicons/primeicons.css +1328 -0
- package/README.md +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
@use 'vos-button-mixins' as *;
|
|
2
|
+
|
|
3
|
+
// ─── visionOS Button Utility Classes (shared with FlyOS desktop shell) ────────
|
|
4
|
+
// Requires @use 'fly-theme' (or equivalent tokens) before this partial.
|
|
5
|
+
|
|
6
|
+
@layer designsystem {
|
|
7
|
+
.vos-btn {
|
|
8
|
+
position: relative;
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
border: none;
|
|
13
|
+
background: transparent;
|
|
14
|
+
color: var(--label-primary);
|
|
15
|
+
font-family: var(--font-family);
|
|
16
|
+
font-weight: var(--btn-font-weight);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
white-space: nowrap;
|
|
19
|
+
overflow: clip;
|
|
20
|
+
padding: 0;
|
|
21
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
22
|
+
|
|
23
|
+
&.sm {
|
|
24
|
+
height: var(--btn-sm-height);
|
|
25
|
+
padding: var(--btn-sm-padding);
|
|
26
|
+
font-size: var(--btn-sm-font-size);
|
|
27
|
+
line-height: var(--btn-sm-line-height);
|
|
28
|
+
gap: var(--btn-sm-gap);
|
|
29
|
+
border-radius: var(--btn-radius-capsule);
|
|
30
|
+
|
|
31
|
+
i, .btn-symbol { font-size: var(--btn-sm-symbol-size); }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&.reg {
|
|
35
|
+
height: var(--btn-reg-height);
|
|
36
|
+
padding: var(--btn-reg-padding);
|
|
37
|
+
font-size: var(--btn-reg-font-size);
|
|
38
|
+
line-height: var(--btn-reg-line-height);
|
|
39
|
+
gap: var(--btn-reg-gap);
|
|
40
|
+
border-radius: var(--btn-radius-capsule);
|
|
41
|
+
|
|
42
|
+
i, .btn-symbol { font-size: var(--btn-reg-symbol-size); }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&.lg {
|
|
46
|
+
height: var(--btn-lg-height);
|
|
47
|
+
padding: var(--btn-lg-padding);
|
|
48
|
+
font-size: var(--btn-lg-font-size);
|
|
49
|
+
line-height: var(--btn-lg-line-height);
|
|
50
|
+
gap: var(--btn-lg-gap);
|
|
51
|
+
border-radius: var(--btn-radius-capsule);
|
|
52
|
+
|
|
53
|
+
i, .btn-symbol { font-size: var(--btn-lg-symbol-size); }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&.rect {
|
|
57
|
+
&.sm { border-radius: var(--btn-radius-rect-sm); }
|
|
58
|
+
&.reg { border-radius: var(--btn-radius-rect-sm); }
|
|
59
|
+
&.lg { border-radius: var(--btn-radius-rect-lg); }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&.platter::before {
|
|
63
|
+
@include vos-platter-layers(inherit);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&:hover:not(:disabled, .selected, .active)::before {
|
|
67
|
+
@include vos-platter-layers(inherit);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&.selected,
|
|
71
|
+
&.active {
|
|
72
|
+
background: var(--btn-selected-bg);
|
|
73
|
+
color: var(--btn-selected-text);
|
|
74
|
+
|
|
75
|
+
&::before { display: none; }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&:disabled, &.disabled {
|
|
79
|
+
cursor: default;
|
|
80
|
+
color: var(--btn-disabled-text);
|
|
81
|
+
pointer-events: none;
|
|
82
|
+
|
|
83
|
+
&::before {
|
|
84
|
+
@include vos-disabled-layers(inherit);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&.danger {
|
|
89
|
+
color: var(--system-red);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&.primary {
|
|
93
|
+
&.platter::before, &:hover:not(:disabled)::before {
|
|
94
|
+
background: linear-gradient(var(--primary-color), var(--primary-color));
|
|
95
|
+
mix-blend-mode: normal;
|
|
96
|
+
opacity: 0.15;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&.selected,
|
|
100
|
+
&.active {
|
|
101
|
+
background: var(--primary-color);
|
|
102
|
+
color: #fff;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.vos-btn-symbol {
|
|
108
|
+
position: relative;
|
|
109
|
+
display: inline-flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
justify-content: center;
|
|
112
|
+
border: none;
|
|
113
|
+
background: transparent;
|
|
114
|
+
color: var(--label-primary);
|
|
115
|
+
font-family: var(--font-family);
|
|
116
|
+
font-weight: var(--btn-symbol-font-weight);
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
overflow: clip;
|
|
119
|
+
border-radius: 500px;
|
|
120
|
+
padding: 0;
|
|
121
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
122
|
+
|
|
123
|
+
&.mini {
|
|
124
|
+
width: var(--btn-symbol-mini);
|
|
125
|
+
height: var(--btn-symbol-mini);
|
|
126
|
+
font-size: var(--btn-symbol-mini-font);
|
|
127
|
+
font-weight: var(--btn-font-weight);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&.sm {
|
|
131
|
+
width: var(--btn-symbol-sm);
|
|
132
|
+
height: var(--btn-symbol-sm);
|
|
133
|
+
font-size: var(--btn-symbol-sm-font);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
&.reg {
|
|
137
|
+
width: var(--btn-symbol-reg);
|
|
138
|
+
height: var(--btn-symbol-reg);
|
|
139
|
+
font-size: var(--btn-symbol-reg-font);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
&.lg {
|
|
143
|
+
width: var(--btn-symbol-lg);
|
|
144
|
+
height: var(--btn-symbol-lg);
|
|
145
|
+
font-size: var(--btn-symbol-lg-font);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
&.xl {
|
|
149
|
+
width: var(--btn-symbol-xl);
|
|
150
|
+
height: var(--btn-symbol-xl);
|
|
151
|
+
font-size: var(--btn-symbol-xl-font);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&.platter::before {
|
|
155
|
+
@include vos-platter-layers(500px);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
&:hover:not(:disabled, .selected, .active)::before {
|
|
159
|
+
@include vos-platter-layers(500px);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
&.selected,
|
|
163
|
+
&.active {
|
|
164
|
+
background: var(--btn-selected-bg);
|
|
165
|
+
color: var(--btn-selected-text);
|
|
166
|
+
|
|
167
|
+
&::before { display: none; }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&:disabled, &.disabled {
|
|
171
|
+
cursor: default;
|
|
172
|
+
color: var(--btn-disabled-text);
|
|
173
|
+
pointer-events: none;
|
|
174
|
+
|
|
175
|
+
&::before {
|
|
176
|
+
@include vos-disabled-layers(500px);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&.danger {
|
|
181
|
+
&:hover:not(:disabled) {
|
|
182
|
+
background: rgb(255 69 58 / 15%);
|
|
183
|
+
color: var(--system-red);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.vos-link {
|
|
189
|
+
display: inline-flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
justify-content: center;
|
|
192
|
+
height: var(--link-height);
|
|
193
|
+
padding: 0 3px;
|
|
194
|
+
border: none;
|
|
195
|
+
background: transparent;
|
|
196
|
+
color: var(--link-color);
|
|
197
|
+
font-family: var(--font-family);
|
|
198
|
+
font-weight: var(--btn-font-weight);
|
|
199
|
+
font-size: var(--link-font-size);
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
white-space: nowrap;
|
|
202
|
+
text-decoration: none;
|
|
203
|
+
border-radius: var(--btn-radius-capsule);
|
|
204
|
+
transition: background 0.15s ease;
|
|
205
|
+
|
|
206
|
+
&:hover {
|
|
207
|
+
background: rgb(60 211 254 / 10%);
|
|
208
|
+
border-radius: var(--btn-radius-rect-sm);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
} // @layer designsystem
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Entry: tokens + html.light-theme / .dark-theme + OS auto dark.
|
|
2
|
+
// Use from shell or Business Apps:
|
|
3
|
+
// stylePreprocessorOptions.includePaths → design-system src folder
|
|
4
|
+
// @use 'scss/fly-theme' or @use 'fly-theme' when scss is on the path.
|
|
5
|
+
@use 'tokens';
|
|
6
|
+
@use 'theme-light';
|
|
7
|
+
@use 'theme-dark';
|
|
8
|
+
|
|
9
|
+
// The inherited `color` for themed documents. Emits a rule, not tokens, so it
|
|
10
|
+
// lives apart from the theme partials — see its header for why the absence of a
|
|
11
|
+
// baseline was the root cause of the recurring black-ink-on-glass bug.
|
|
12
|
+
@use 'ink-baseline';
|
|
13
|
+
|
|
14
|
+
// Business-app content surfaces (--ink-*/--bg-*/--line-*, the sp/r/text/z/shadow
|
|
15
|
+
// scales) consumed by the `fly-*` component library. Deliberately a family apart
|
|
16
|
+
// from the shell-chrome tokens above — see the partial's header for why, and for
|
|
17
|
+
// the 12 platform-owned tokens it must never redefine.
|
|
18
|
+
@use 'app-surface-tokens';
|
|
19
|
+
|
|
20
|
+
// Global CSS the app-surface components rely on: the `[data-tooltip]` hover label
|
|
21
|
+
// (written directly by fly-button / fly-icon-button / fly-status-badge) and the
|
|
22
|
+
// `.mono` tabular-numerals utility. Emits CSS, so it belongs here rather than in
|
|
23
|
+
// the per-component styles.
|
|
24
|
+
@use 'app-surface-utilities';
|
|
25
|
+
@use 'theme-auto';
|
|
26
|
+
@use 'native-select'; // native <select> popups follow the theme's color-scheme (see the partial)
|
|
27
|
+
@use 'menu-anchor'; // the trigger of an open fly-context-menu reads as pressed (see the partial)
|
|
@@ -0,0 +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 Liquid-Glass 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
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// ─── The trigger of an OPEN fly-context-menu ─────────────────────────────────
|
|
2
|
+
// `fly-context-menu` stamps `data-fly-menu-open` on whatever element is passed as its `[anchor]`
|
|
3
|
+
// for as long as the menu is up (it also sets aria-expanded / aria-haspopup — see the component).
|
|
4
|
+
// This paints the matching visual, so a ⋯ button reads as pressed instead of sitting idle while
|
|
5
|
+
// its own menu floats above the page.
|
|
6
|
+
// Imported by `fly-theme`, so the shell and every External App remote get it from the same import
|
|
7
|
+
// that gives them the theme. It has to be global: the anchor lives inside the CONSUMER's
|
|
8
|
+
// encapsulated component, so a DS component stylesheet could never reach it — the same reason
|
|
9
|
+
// `_native-select.scss` lives here. Doing it centrally is the point: the alternative is every
|
|
10
|
+
// caller hand-binding `[class.active]="menuOpen()"`, which is why the indicator was missing
|
|
11
|
+
// everywhere.
|
|
12
|
+
// Specificity: the attribute is repeated to reach (0,3,0). Angular's emulated encapsulation rewrites
|
|
13
|
+
// a component rule like `.btn--ghost { background: transparent }` to `.btn--ghost[_ngcontent-x]`
|
|
14
|
+
// (0,2,0) and injects it into <head> AFTER this stylesheet, so a single `[data-fly-menu-open]`
|
|
15
|
+
// (0,1,0) — or even a doubled one, which ties and then loses on source order — would be silently
|
|
16
|
+
// overridden by the very base style it needs to beat. `!important` would win too, but would also
|
|
17
|
+
// stop a consumer from restyling the state at all; this leaves that door open.
|
|
18
|
+
// `--surface-active` is the DS's selected/active tint and is defined in BOTH themes, so this needs
|
|
19
|
+
// no light/dark pair of its own.
|
|
20
|
+
[data-fly-menu-open][data-fly-menu-open][data-fly-menu-open] {
|
|
21
|
+
background-color: var(--surface-active);
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// ─── Native <select> popup — the ONE fix, platform-wide ──────────────────────
|
|
2
|
+
// Imported by `fly-theme`, so the shell and every External App remote get it from the
|
|
3
|
+
// same import that gives them the theme. Do not copy this rule into an app.
|
|
4
|
+
// THE BUG IT KILLS
|
|
5
|
+
// A themed `<select>` carries a translucent background (the shell's field tokens are glass:
|
|
6
|
+
// `rgb(255 255 255 / 6%)` in dark) and light-on-dark text. That is correct for the CLOSED
|
|
7
|
+
// control. But the OPEN option list is OS chrome: Chromium paints its rows with the select's
|
|
8
|
+
// own `background-color`, composited over a WHITE popup backing. A 6%-white fill over white
|
|
9
|
+
// is white — and the inherited light text lands on it, so the list reads as blank/transparent.
|
|
10
|
+
// The user sees an empty dropdown. It is theme-dependent and easy to miss in light mode.
|
|
11
|
+
// THE FIX
|
|
12
|
+
// Repaint the rows with the `Canvas` / `CanvasText` system pair. Those two are the only colours
|
|
13
|
+
// the native listbox reliably honours, and they FOLLOW `color-scheme` — which the theme already
|
|
14
|
+
// sets on `<html>` (`color-scheme: dark` in `_theme-dark.scss`, `light` in `_theme-light.scss`)
|
|
15
|
+
// and which inherits down to every select. So one rule is automatically correct in both themes,
|
|
16
|
+
// with no per-app pinning.
|
|
17
|
+
// DO NOT pin `color-scheme` on a select to "fix" a popup. Three apps did that independently
|
|
18
|
+
// (dashboard + surveys pinned `light`, settings pinned `dark`) — each one fights the inherited
|
|
19
|
+
// value and is wrong in the opposite theme. Pinning is what this rule replaces.
|
|
20
|
+
// Prefer `<fly-select>` for new work: it renders its own panel, so it has no OS chrome to fight
|
|
21
|
+
// and it can search/multi-select. This rule is the safety net for the native selects that remain.
|
|
22
|
+
option,
|
|
23
|
+
optgroup {
|
|
24
|
+
background-color: Canvas;
|
|
25
|
+
color: CanvasText;
|
|
26
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// ─── Dark Theme Variables (shared mixin to avoid duplication) ────────────────
|
|
2
|
+
// NOTE: this mixin is currently never @include-d — it is a leftover. Kept in sync with
|
|
3
|
+
// _theme-dark.scss anyway so it cannot resurrect the brand orange if someone wires it up.
|
|
4
|
+
@mixin dark-theme-vars {
|
|
5
|
+
--primary-light: color-mix(in srgb, var(--accent) 18%, transparent);
|
|
6
|
+
--surface-ground: #0e0e10;
|
|
7
|
+
--surface-section: rgb(28 28 30 / 85%);
|
|
8
|
+
--surface-card: rgb(28 28 30 / 70%);
|
|
9
|
+
--surface-border: rgb(255 255 255 / 10%);
|
|
10
|
+
--surface-hover: rgb(255 255 255 / 8%);
|
|
11
|
+
--text-color: #f2f2f7;
|
|
12
|
+
--text-color-secondary: rgb(242 242 247 / 65%);
|
|
13
|
+
--glass-bg: rgb(25 25 28 / 72%);
|
|
14
|
+
--glass-bg-elevated: rgb(38 38 42 / 82%);
|
|
15
|
+
|
|
16
|
+
// Ink for text ON `--glass-bg-elevated` — see `_theme-light.scss` for why this is its own pair.
|
|
17
|
+
--glass-ink-elevated: rgb(242 242 247 / 96%);
|
|
18
|
+
--glass-ink-elevated-secondary: rgb(242 242 247 / 65%);
|
|
19
|
+
--glass-border: rgb(255 255 255 / 12%);
|
|
20
|
+
--glass-shadow: 0 8px 40px rgb(0 0 0 / 35%);
|
|
21
|
+
--glass-inner-glow: inset 0 1px 0 rgb(255 255 255 / 8%);
|
|
22
|
+
--window-bg: rgb(22 22 24 / 55%);
|
|
23
|
+
--window-radius: 46px;
|
|
24
|
+
--window-shadow: 0 24px 80px rgb(0 0 0 / 40%);
|
|
25
|
+
--window-border: rgb(255 255 255 / 12%);
|
|
26
|
+
--titlebar-bg: transparent;
|
|
27
|
+
--dock-bg: rgb(20 20 20 / 24%);
|
|
28
|
+
--dock-border: 1px solid rgb(255 255 255 / 8%);
|
|
29
|
+
--dock-blur: 40px;
|
|
30
|
+
--dock-shadow: 0 8px 32px rgb(0 0 0 / 60%),
|
|
31
|
+
inset 0 1px 0 rgb(255 255 255 / 5%);
|
|
32
|
+
--menubar-bg: rgb(14 14 16 / 60%);
|
|
33
|
+
--menubar-border: rgb(255 255 255 / 6%);
|
|
34
|
+
--menubar-text: rgb(242 242 247 / 100%);
|
|
35
|
+
--sidebar-bg: rgb(20 20 22 / 60%);
|
|
36
|
+
--sidebar-border: rgb(255 255 255 / 8%);
|
|
37
|
+
--focus-ring: color-mix(in srgb, var(--accent) 60%, transparent);
|
|
38
|
+
|
|
39
|
+
// Opaque Business App surfaces (mirror light-theme semantic set for embeds).
|
|
40
|
+
--label-primary: var(--text-color);
|
|
41
|
+
--label-secondary: var(--text-color-secondary);
|
|
42
|
+
--label-tertiary: rgb(242 242 247 / 42%);
|
|
43
|
+
--separator-primary: rgb(255 255 255 / 14%);
|
|
44
|
+
--separator: var(--separator-primary);
|
|
45
|
+
--fill-secondary: rgb(255 255 255 / 10%);
|
|
46
|
+
--fill-tertiary: rgb(255 255 255 / 6%);
|
|
47
|
+
--material-glass: rgb(28 28 30 / 92%);
|
|
48
|
+
--fly-color-surface: rgb(28 28 30 / 95%);
|
|
49
|
+
--fly-color-surface-alt: rgb(38 38 42 / 90%);
|
|
50
|
+
--fly-color-border: rgb(255 255 255 / 12%);
|
|
51
|
+
--fly-color-text-muted: var(--text-color-secondary);
|
|
52
|
+
--fly-color-primary: var(--primary-color);
|
|
53
|
+
--fly-color-primary-hover: var(--primary-hover);
|
|
54
|
+
--fly-color-danger: var(--system-red);
|
|
55
|
+
--fly-shadow-md: 0 4px 16px rgb(0 0 0 / 35%);
|
|
56
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// ─── Dark theme (`html.dark-theme`, persisted `dark`) ────────────────────────
|
|
2
|
+
// Shell Appearance label "Dark" / i18n `settings.theme.dark`.
|
|
3
|
+
// VisionOS-style translucent dark glass: semi-transparent surfaces + backdrop blur.
|
|
4
|
+
html.dark-theme {
|
|
5
|
+
/* Native selects / scrollbars follow OS form appearance; keep in sync with light-on-glass labels. */
|
|
6
|
+
color-scheme: dark;
|
|
7
|
+
|
|
8
|
+
--primary-light: color-mix(in srgb, var(--accent) 20%, transparent);
|
|
9
|
+
|
|
10
|
+
// ── FlyOS platform accent ────────────────────────────────────────────────
|
|
11
|
+
// Platform-owned accent palette (violet). Theme-agnostic — same values as
|
|
12
|
+
// the light theme. App-specific token remaps live in each External App's
|
|
13
|
+
// own remote stylesheet (e.g. Circles' circles-shell-overrides.scss), NOT
|
|
14
|
+
// here.
|
|
15
|
+
// NOTE: --focus-ring is also platform-owned; its authoritative value is the
|
|
16
|
+
// orange declaration near the end of this block (a later declaration in the
|
|
17
|
+
// same block always wins, so a second definition here would be dead code).
|
|
18
|
+
--purple: #9333EA;
|
|
19
|
+
|
|
20
|
+
// ── THE accent knob ────────────────────────────────────────────────────────
|
|
21
|
+
// The one value that defines the platform's accent. Everything else derives from it
|
|
22
|
+
// (--primary-color, --accent-soft, --focus-ring, the fly-context-menu gradient, …), so a tenant
|
|
23
|
+
// or user override is a single write: FlyThemeService.applyAccent('#0aa') re-skins the platform.
|
|
24
|
+
// Never copy this hex elsewhere — a copy silently stops tracking the override.
|
|
25
|
+
--accent: #9333EA;
|
|
26
|
+
|
|
27
|
+
// Alias of --accent, NOT a second brand colour. It was referenced ~163 times across the DS
|
|
28
|
+
// and shell and defined nowhere, so every `var(--primary-color)` silently shipped
|
|
29
|
+
// its orange fallback — that fallback WAS the platform's accent in practice. Defining it
|
|
30
|
+
// here turns all of those purple at once. An app may still scope its own (dashboard-app
|
|
31
|
+
// pins blue); a local override wins inside its subtree, as before.
|
|
32
|
+
--primary-color: var(--accent);
|
|
33
|
+
|
|
34
|
+
// DERIVED, never a copy. A hardcoded rgba(147,51,234,…) here would not follow a runtime
|
|
35
|
+
// accent override (see FlyThemeService.applyAccent) — you would get the new accent with
|
|
36
|
+
// the old tint beside it. Every accent-derived value must color-mix off --accent.
|
|
37
|
+
--accent-soft: color-mix(in srgb, var(--accent) 18%, transparent);
|
|
38
|
+
|
|
39
|
+
// Scrim — deeper veil for drawers / dialogs over translucent glass
|
|
40
|
+
--scrim: rgb(0 0 0 / 50%);
|
|
41
|
+
|
|
42
|
+
// Semantic feedback — bright fg on translucent bg
|
|
43
|
+
--danger: #ff6b6b;
|
|
44
|
+
--danger-bg: rgb(255 59 48 / 20%);
|
|
45
|
+
--danger-line: rgb(255 59 48 / 40%);
|
|
46
|
+
--warning: #FEBC2E;
|
|
47
|
+
--warning-bg: rgb(254 188 46 / 18%);
|
|
48
|
+
--warning-fg: rgb(255 255 255 / 96%);
|
|
49
|
+
|
|
50
|
+
// ── Material glass primitives ───────────────────────────────────────────────
|
|
51
|
+
--material-glass: rgb(28 28 31 / 65%);
|
|
52
|
+
--material-glass-blur: 40px;
|
|
53
|
+
--material-glass-stroke: rgb(255 255 255 / 16%);
|
|
54
|
+
--material-glass-specular: inset 0 1px 0 rgb(255 255 255 / 10%),
|
|
55
|
+
inset 0 -1px 0 rgb(255 255 255 / 5%);
|
|
56
|
+
|
|
57
|
+
// ── Surfaces ────────────────────────────────────────────────────────────────
|
|
58
|
+
--app-content-plate: transparent;
|
|
59
|
+
--surface-ground: #0e0e10;
|
|
60
|
+
--surface-section: rgb(22 22 26 / 78%);
|
|
61
|
+
--surface-card: rgb(28 28 32 / 55%);
|
|
62
|
+
|
|
63
|
+
// Opaque surface for floating overlays that sit ABOVE content and must stay
|
|
64
|
+
// fully legible — popovers, menus, tooltips. Unlike the translucent glass
|
|
65
|
+
// --surface-card (which reads whatever is behind it), this is solid.
|
|
66
|
+
--surface-overlay: #1f1f24;
|
|
67
|
+
--surface-border: rgb(255 255 255 / 14%);
|
|
68
|
+
--surface-hover: rgb(255 255 255 / 8%);
|
|
69
|
+
|
|
70
|
+
// Selected/active row tint — one step stronger than hover. Composited over an opaque panel, so a
|
|
71
|
+
// translucent tint is safe (unlike the glass surfaces above it needs no reduced-transparency pair).
|
|
72
|
+
--surface-active: rgb(255 255 255 / 14%);
|
|
73
|
+
|
|
74
|
+
// ── Labels & text ───────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/* `:root` / `html.light-theme` set dark `--label-*`; dark shell must force light-on-dark. */
|
|
77
|
+
// Steps 96 / 72 / 48, mirroring the light theme's 92 / 72 / 55. Secondary and tertiary were
|
|
78
|
+
// 65%/42%, which missed AA on the plate once a BRIGHT wallpaper (`alpine-lake`) tinted it
|
|
79
|
+
// through the glass — the dark theme's mirror image of the light-theme bug, just milder.
|
|
80
|
+
// Raised here rather than by thickening `--glass-bg` further, to keep the glass translucent.
|
|
81
|
+
--label-primary: rgb(255 255 255 / 96%);
|
|
82
|
+
--label-secondary: rgb(255 255 255 / 72%);
|
|
83
|
+
--label-tertiary: rgb(255 255 255 / 50%);
|
|
84
|
+
--separator-primary: rgb(255 255 255 / 14%);
|
|
85
|
+
--separator: var(--separator-primary);
|
|
86
|
+
--fill-secondary: rgb(255 255 255 / 10%);
|
|
87
|
+
--fill-tertiary: rgb(255 255 255 / 6%);
|
|
88
|
+
--text-color: var(--label-primary);
|
|
89
|
+
--text-color-secondary: var(--label-secondary);
|
|
90
|
+
|
|
91
|
+
// ── Fly alias tokens ────────────────────────────────────────────────────────
|
|
92
|
+
--fly-color-surface: #1c1c1f;
|
|
93
|
+
--fly-color-surface-alt: #242428;
|
|
94
|
+
--fly-color-border: rgb(255 255 255 / 14%);
|
|
95
|
+
--fly-color-text-muted: var(--text-color-secondary);
|
|
96
|
+
--fly-color-primary: var(--primary-color);
|
|
97
|
+
--fly-color-primary-hover: var(--primary-hover);
|
|
98
|
+
--fly-color-danger: var(--system-red);
|
|
99
|
+
--fly-shadow-md: 0 4px 16px rgb(0 0 0 / 42%);
|
|
100
|
+
|
|
101
|
+
// ── Glass system ────────────────────────────────────────────────────────────
|
|
102
|
+
--glass-bg: rgb(22 22 26 / 72%);
|
|
103
|
+
--glass-bg-elevated: rgb(30 30 36 / 82%);
|
|
104
|
+
|
|
105
|
+
// Ink for text ON `--glass-bg-elevated` — see the light theme for why this is its own pair.
|
|
106
|
+
--glass-ink-elevated: rgb(255 255 255 / 96%);
|
|
107
|
+
--glass-ink-elevated-secondary: rgb(255 255 255 / 65%);
|
|
108
|
+
--glass-blur: 40px;
|
|
109
|
+
--glass-border: var(--material-glass-stroke);
|
|
110
|
+
--glass-shadow: 0 16px 60px rgb(0 0 0 / 50%);
|
|
111
|
+
--glass-inner-glow: var(--material-glass-specular);
|
|
112
|
+
|
|
113
|
+
// ── Layout metrics (size, not color — same as light) ────────────────────────
|
|
114
|
+
--card-radius: 16px;
|
|
115
|
+
--card-padding: 20px;
|
|
116
|
+
--card-gap: 14px;
|
|
117
|
+
|
|
118
|
+
// ── Apps launcher card tokens — dark glass variant ──────────────────────────
|
|
119
|
+
--launcher-card-bg: rgb(28 28 32 / 45%);
|
|
120
|
+
--launcher-card-border: rgb(255 255 255 / 12%);
|
|
121
|
+
--launcher-card-blur: 50px;
|
|
122
|
+
--launcher-card-radius: 16px;
|
|
123
|
+
--launcher-card-icon-bg: rgb(255 255 255 / 8%);
|
|
124
|
+
--launcher-card-icon-border: rgb(255 255 255 / 14%);
|
|
125
|
+
--launcher-card-icon-blur: 4px;
|
|
126
|
+
--launcher-card-title: rgb(255 255 255 / 92%);
|
|
127
|
+
--launcher-card-desc: rgb(255 255 255 / 58%);
|
|
128
|
+
|
|
129
|
+
// ── Window chrome ───────────────────────────────────────────────────────────
|
|
130
|
+
// 42% was too thin to be a dependable substrate: over the brightest shipped wallpapers
|
|
131
|
+
// (`alpine-lake`, `cherry-blossom`) the plate stayed light enough that white body text fell
|
|
132
|
+
// to ~2.9:1. Raised to match `--glass-bg`, which is the same surface one layer out.
|
|
133
|
+
--window-bg: rgb(20 20 24 / 70%);
|
|
134
|
+
--window-radius: 24px;
|
|
135
|
+
--window-blur: 30px;
|
|
136
|
+
--window-backdrop-saturate: 160%;
|
|
137
|
+
--window-shadow: 0 36px 120px rgb(0 0 0 / 52%);
|
|
138
|
+
--window-border: var(--material-glass-stroke);
|
|
139
|
+
--window-specular: inset 0 1px 0 rgb(255 255 255 / 12%),
|
|
140
|
+
inset 0 -1px 0 rgb(255 255 255 / 6%);
|
|
141
|
+
--window-content-scrim: transparent;
|
|
142
|
+
|
|
143
|
+
// ── Titlebar ────────────────────────────────────────────────────────────────
|
|
144
|
+
--titlebar-bg: transparent;
|
|
145
|
+
--titlebar-height: 44px;
|
|
146
|
+
--titlebar-padding: 0 14px;
|
|
147
|
+
|
|
148
|
+
// ── Dock ────────────────────────────────────────────────────────────────────
|
|
149
|
+
--dock-bg: rgb(18 18 22 / 60%);
|
|
150
|
+
--dock-border: var(--material-glass-stroke);
|
|
151
|
+
--dock-blur: 50px;
|
|
152
|
+
|
|
153
|
+
// ── Menubar ─────────────────────────────────────────────────────────────────
|
|
154
|
+
--menubar-bg: rgb(14 14 18 / 80%);
|
|
155
|
+
--menubar-border: var(--material-glass-stroke);
|
|
156
|
+
--menubar-text: var(--label-primary);
|
|
157
|
+
--menubar-blur: 40px;
|
|
158
|
+
--menubar-radius: 24px;
|
|
159
|
+
|
|
160
|
+
// ── Sidebar ─────────────────────────────────────────────────────────────────
|
|
161
|
+
--sidebar-bg: rgb(18 18 22 / 72%);
|
|
162
|
+
--sidebar-border: var(--material-glass-stroke);
|
|
163
|
+
--sidebar-width: 60px;
|
|
164
|
+
--sidebar-icon-size: 36px;
|
|
165
|
+
--sidebar-radius: 28px;
|
|
166
|
+
--focus-ring: color-mix(in srgb, var(--accent) 60%, transparent);
|
|
167
|
+
|
|
168
|
+
// ── Button selected state — white-tint pill on dark surface ─────────────────
|
|
169
|
+
--btn-selected-bg: rgb(255 255 255 / 15%);
|
|
170
|
+
--btn-selected-text: #fff;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── Reduced-transparency fallback — restore fully opaque / zero-blur values ──
|
|
174
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
175
|
+
html.dark-theme {
|
|
176
|
+
--material-glass: #1c1c1f;
|
|
177
|
+
--material-glass-blur: 0px;
|
|
178
|
+
--app-content-plate: #121214;
|
|
179
|
+
--surface-section: #161618;
|
|
180
|
+
--surface-card: #1c1c1f;
|
|
181
|
+
--surface-overlay: #1c1c1f;
|
|
182
|
+
--glass-bg: #161618;
|
|
183
|
+
--glass-bg-elevated: #1e1e22;
|
|
184
|
+
--glass-blur: 0px;
|
|
185
|
+
--window-bg: #141416;
|
|
186
|
+
--window-blur: 0px;
|
|
187
|
+
--window-backdrop-saturate: 100%;
|
|
188
|
+
--dock-bg: #121214;
|
|
189
|
+
--dock-blur: 0px;
|
|
190
|
+
--menubar-bg: #0e0e10;
|
|
191
|
+
--menubar-blur: 0px;
|
|
192
|
+
--sidebar-bg: #121214;
|
|
193
|
+
--launcher-card-bg: #1c1c1f;
|
|
194
|
+
--launcher-card-blur: 0px;
|
|
195
|
+
}
|
|
196
|
+
}
|