@uniflowed/stylex 0.0.0-alpha.9 → 0.2.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/index.js +41 -10
- package/native-props.js +40 -0
- package/native.js +40 -0
- package/package.json +14 -4
- package/preset.js +21 -13
- package/props.js +8 -1
- package/theme.js +34 -38
- package/tokens.stylex.js +63 -34
package/index.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
//
|
|
22
22
|
// 1. **A token set that already exists.** `@uniflowed/stylex/tokens.stylex.js`
|
|
23
23
|
// is a real `stylex.defineVars` module — colour roles, a type scale,
|
|
24
|
-
// spacing, radii
|
|
24
|
+
// spacing, radii and motion — so a project has a coherent palette
|
|
25
25
|
// without authoring one, and gets it as `:root` custom properties the build
|
|
26
26
|
// inlined rather than as anything computed in a browser.
|
|
27
27
|
// 2. **A base layer over those tokens.** `@uniflowed/stylex/preset` is
|
|
@@ -39,21 +39,52 @@
|
|
|
39
39
|
// configuration flag. The preset is reached by importing it, so a project that
|
|
40
40
|
// does not import it pays nothing, and one that imports half of it keeps half.
|
|
41
41
|
//
|
|
42
|
+
// # The half of a component catalogue that lives here
|
|
43
|
+
//
|
|
44
|
+
// "Not a component library" is a rule about what this package exports, and it
|
|
45
|
+
// was read for a while as a rule about what it is responsible for. It is not.
|
|
46
|
+
// About twenty of the catalogue `@uniflowed/ui` is measured against — Badge,
|
|
47
|
+
// Card, Button, Input, Textarea, Label, Aspect Ratio and the rest — have no
|
|
48
|
+
// behaviour at all: no role, no state machine, no keyboard map. A headless
|
|
49
|
+
// package cannot ship them, because with the styles taken out a `Badge` is a
|
|
50
|
+
// `<span>`. **They are this module's half of the split, and the answer is a
|
|
51
|
+
// style function rather than a component** (ubugeeei-prod/uf#298).
|
|
52
|
+
//
|
|
53
|
+
// `buttonStyles({ tone, size })`, `cardStyles()`, `textStyles({ size, tone })`
|
|
54
|
+
// and `fieldStyles()` are already that answer for four of them, and the shape
|
|
55
|
+
// is the point: a caller spreads `{ className }` onto their own `<button>`, so
|
|
56
|
+
// they keep `type="submit"`, `formAction`, the ref and every attribute a
|
|
57
|
+
// wrapper would have had to remember to forward. Nothing here appears in
|
|
58
|
+
// `@uniflowed/ui`'s types and nothing here renders an element — the rule holds
|
|
59
|
+
// exactly as written; what changed is that the gap is now assigned rather than
|
|
60
|
+
// left between the two packages.
|
|
61
|
+
//
|
|
62
|
+
// `crates/uf_lib/src/ui.rs` records the assignment component by component, and
|
|
63
|
+
// `cargo test -p uf_lib` holds it to this file in both directions: an entry
|
|
64
|
+
// there may not name a style `./preset.js` does not export, and a style
|
|
65
|
+
// exported here must be named by some entry. A `badgeStyles` is the way to
|
|
66
|
+
// close the Badge gap; a `<Badge>` in the other package is not.
|
|
67
|
+
//
|
|
42
68
|
// # Where the token values come from
|
|
43
69
|
//
|
|
44
|
-
// `@uniflowed/brand` owns uf's visual identity — the
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
70
|
+
// `@uniflowed/brand` owns uf's visual identity — the logo, its spectrum, the
|
|
71
|
+
// type and spacing scales — and it stays there. The interface borrows brand's
|
|
72
|
+
// type and spacing, and chooses its own colours and corners: a UI dressed in
|
|
73
|
+
// the logo's cyan-to-magenta spectrum and round corners looks generated, so
|
|
74
|
+
// `./tokens.stylex.js` holds a neutral-first palette of its own
|
|
75
|
+
// (`docs/ui-visual-language.md` says why). Brand could not own the token
|
|
76
|
+
// module anyway: a StyleX token's name is computed by the compiler from the
|
|
77
|
+
// binding and key it was declared under, and `defineVars` accepts only
|
|
78
|
+
// literals. So `./tokens.stylex.js` is where the semantic roles — `accent`,
|
|
79
|
+
// `ink`, `canvas` — get their values, which is the layer a design system needs
|
|
80
|
+
// and an identity does not have.
|
|
52
81
|
//
|
|
53
82
|
// # Readiness
|
|
54
83
|
//
|
|
55
84
|
// **Implemented.** `props`, `create`, `defineVars`, `createTheme`, pseudo-class
|
|
56
|
-
// and pseudo-element conditions,
|
|
85
|
+
// and pseudo-element conditions, the state a headless part announces
|
|
86
|
+
// (`:is([aria-selected=true])` — `crates/uf_stylex/src/condition.rs` says why
|
|
87
|
+
// that shape and no wider), at-rule conditions (`@media`, `@supports`),
|
|
57
88
|
// shorthand-versus-longhand ordering, unit inference, the preset tokens, the
|
|
58
89
|
// base layer, and the shipped themes. Every one of them is compiled: the
|
|
59
90
|
// runtime half of this package is `props` and three functions that throw.
|
package/native-props.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
export type NativeStyleProps = { readonly style: { readonly [string]: string | number } };
|
|
4
|
+
|
|
5
|
+
/** Return null for web namespaces; mixing renderer outputs is an error. */
|
|
6
|
+
export function nativeProps(
|
|
7
|
+
styles: $ReadOnlyArray<mixed>,
|
|
8
|
+
nativeOnly: boolean = false,
|
|
9
|
+
): NativeStyleProps | null {
|
|
10
|
+
let native = false;
|
|
11
|
+
let web = false;
|
|
12
|
+
const merged: { [string]: string | number } = {};
|
|
13
|
+
const pending = [...styles];
|
|
14
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
15
|
+
const value = pending[index];
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
pending.splice(index + 1, 0, ...value);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (value == null || typeof value !== "object") continue;
|
|
21
|
+
// Static objects written by the compiler; validate their scalar fields.
|
|
22
|
+
const namespace: { [string]: mixed } = value as $FlowFixMe;
|
|
23
|
+
if (namespace.$$css === true) web = true;
|
|
24
|
+
if (namespace.$$native !== true) {
|
|
25
|
+
if (nativeOnly) throw new Error("StyleX native: expected a compiled native namespace");
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
native = true;
|
|
29
|
+
for (const property of Object.keys(namespace)) {
|
|
30
|
+
if (property === "$$native") continue;
|
|
31
|
+
const entry = namespace[property];
|
|
32
|
+
if (typeof entry !== "string" && typeof entry !== "number") {
|
|
33
|
+
throw new Error(`StyleX native: invalid compiled value for ${property}`);
|
|
34
|
+
}
|
|
35
|
+
merged[property] = entry;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (native && web) throw new Error("StyleX: cannot mix native style objects with web classes");
|
|
39
|
+
return native ? { style: merged } : null;
|
|
40
|
+
}
|
package/native.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type { ViewStyle, TextStyle, ImageStyle } from "react-native";
|
|
3
|
+
import { nativeRuntimeRequired } from "@uniflowed/core/native";
|
|
4
|
+
import { nativeProps } from "./native-props.js";
|
|
5
|
+
|
|
6
|
+
// Use one property map: a ViewStyle | TextStyle union would accept an invalid
|
|
7
|
+
// fontWeight through its ViewStyle branch, whose optional fields all match.
|
|
8
|
+
export type NativeStyle = $ReadOnly<{
|
|
9
|
+
...$Exact<ViewStyle>,
|
|
10
|
+
...$Exact<TextStyle>,
|
|
11
|
+
...$Exact<ImageStyle>,
|
|
12
|
+
}>;
|
|
13
|
+
export type NativeStyleArgument<Style> =
|
|
14
|
+
| Style
|
|
15
|
+
| false
|
|
16
|
+
| null
|
|
17
|
+
| void
|
|
18
|
+
| $ReadOnlyArray<NativeStyleArgument<Style>>;
|
|
19
|
+
type StyleFields<Value> = Value extends $ReadOnlyArray<infer Item>
|
|
20
|
+
? StyleFields<Item>
|
|
21
|
+
: Value extends (false | null | void)
|
|
22
|
+
? {}
|
|
23
|
+
: Value;
|
|
24
|
+
|
|
25
|
+
/** Compile-time namespaces, checked against the app's React Native styles. */
|
|
26
|
+
export function create<Styles extends { readonly [string]: NativeStyle }>(styles: Styles): Styles {
|
|
27
|
+
return nativeRuntimeRequired("@uniflowed/stylex/native", "stylex.create");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Native-only props retain the authored property types and never add className. */
|
|
31
|
+
export function props<Args extends $ReadOnlyArray<NativeStyleArgument<NativeStyle>>>(
|
|
32
|
+
...styles: Args
|
|
33
|
+
): { readonly style: Partial<StyleFields<Args>> } {
|
|
34
|
+
const result = nativeProps(styles, true);
|
|
35
|
+
// The compiler validates the native subset and preserves every scalar value.
|
|
36
|
+
// The merger copies those fields, but its dynamic keys cannot retain Style.
|
|
37
|
+
return (result ?? { style: {} }) as $FlowFixMe;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const stylex = { create, props };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniflowed/stylex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Flow declarations for @uniflowed/stylex, part of the Unified Toolchain for Flow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,12 +15,22 @@
|
|
|
15
15
|
"./props": "./props.js",
|
|
16
16
|
"./preset": "./preset.js",
|
|
17
17
|
"./theme": "./theme.js",
|
|
18
|
-
"./tokens.stylex.js": "./tokens.stylex.js"
|
|
18
|
+
"./tokens.stylex.js": "./tokens.stylex.js",
|
|
19
|
+
"./native": "./native.js"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
|
-
"*.js"
|
|
22
|
+
"*.js",
|
|
23
|
+
"!*.test.js"
|
|
22
24
|
],
|
|
23
25
|
"dependencies": {
|
|
24
|
-
"@uniflowed/core": "0.
|
|
26
|
+
"@uniflowed/core": "0.2.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react-native": ">=0.86.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"react-native": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
25
35
|
}
|
|
26
36
|
}
|
package/preset.js
CHANGED
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
//
|
|
21
21
|
// It is the only module in the package that has opinions. `props` is a merge,
|
|
22
22
|
// the tokens are names, the themes are values; this is where uf says a card has
|
|
23
|
-
// a
|
|
24
|
-
// everything else, and a bundler drops
|
|
25
|
-
// references them.
|
|
23
|
+
// a 6px radius and a 1px border rather than a shadow. A project that wants its
|
|
24
|
+
// own look drops this import and keeps everything else, and a bundler drops
|
|
25
|
+
// the rules with it because nothing else references them.
|
|
26
26
|
//
|
|
27
27
|
// # Why functions rather than exported namespaces
|
|
28
28
|
//
|
|
@@ -84,13 +84,14 @@ const surfaces = stylex.create({
|
|
|
84
84
|
borderColor: ufTokens.border,
|
|
85
85
|
borderRadius: ufTokens.radiusLg,
|
|
86
86
|
padding: ufTokens.space6,
|
|
87
|
-
boxShadow: ufTokens.shadowCard,
|
|
88
87
|
},
|
|
89
88
|
panel: {
|
|
90
89
|
backgroundColor: ufTokens.surface,
|
|
91
|
-
|
|
90
|
+
borderWidth: "1px",
|
|
91
|
+
borderStyle: "solid",
|
|
92
|
+
borderColor: ufTokens.border,
|
|
93
|
+
borderRadius: ufTokens.radiusLg,
|
|
92
94
|
padding: ufTokens.space6,
|
|
93
|
-
boxShadow: ufTokens.shadowPanel,
|
|
94
95
|
},
|
|
95
96
|
sunken: {
|
|
96
97
|
backgroundColor: ufTokens.sunken,
|
|
@@ -132,7 +133,10 @@ const buttons = stylex.create({
|
|
|
132
133
|
borderColor: "transparent",
|
|
133
134
|
cursor: "pointer",
|
|
134
135
|
transitionProperty: "background-color, border-color, color",
|
|
135
|
-
transitionDuration:
|
|
136
|
+
transitionDuration: {
|
|
137
|
+
default: ufTokens.durationFast,
|
|
138
|
+
"@media (prefers-reduced-motion: reduce)": "0s",
|
|
139
|
+
},
|
|
136
140
|
transitionTimingFunction: ufTokens.easing,
|
|
137
141
|
// The ring is drawn only for a keyboard focus, which is the whole reason
|
|
138
142
|
// `:focus-visible` exists: a mouse click should not light the control up.
|
|
@@ -191,7 +195,7 @@ const fields = stylex.create({
|
|
|
191
195
|
borderWidth: "1px",
|
|
192
196
|
borderStyle: "solid",
|
|
193
197
|
borderColor: ufTokens.border,
|
|
194
|
-
borderRadius: ufTokens.
|
|
198
|
+
borderRadius: ufTokens.radiusMd,
|
|
195
199
|
paddingBlock: ufTokens.space2,
|
|
196
200
|
paddingInline: ufTokens.space3,
|
|
197
201
|
outlineWidth: { default: "0", ":focus-visible": "2px" },
|
|
@@ -226,9 +230,11 @@ const overlays = stylex.create({
|
|
|
226
230
|
backgroundColor: ufTokens.surface,
|
|
227
231
|
color: ufTokens.ink,
|
|
228
232
|
fontFamily: ufTokens.fontSans,
|
|
229
|
-
|
|
233
|
+
borderWidth: "1px",
|
|
234
|
+
borderStyle: "solid",
|
|
235
|
+
borderColor: ufTokens.border,
|
|
236
|
+
borderRadius: ufTokens.radiusLg,
|
|
230
237
|
padding: ufTokens.space6,
|
|
231
|
-
boxShadow: ufTokens.shadowPanel,
|
|
232
238
|
},
|
|
233
239
|
});
|
|
234
240
|
|
|
@@ -243,7 +249,6 @@ const menus = stylex.create({
|
|
|
243
249
|
borderStyle: "solid",
|
|
244
250
|
borderColor: ufTokens.border,
|
|
245
251
|
borderRadius: ufTokens.radiusMd,
|
|
246
|
-
boxShadow: ufTokens.shadowPanel,
|
|
247
252
|
},
|
|
248
253
|
item: {
|
|
249
254
|
display: "flex",
|
|
@@ -315,7 +320,10 @@ const controls = stylex.create({
|
|
|
315
320
|
color: ufTokens.accentInk,
|
|
316
321
|
cursor: "pointer",
|
|
317
322
|
transitionProperty: "background-color, border-color",
|
|
318
|
-
transitionDuration:
|
|
323
|
+
transitionDuration: {
|
|
324
|
+
default: ufTokens.durationFast,
|
|
325
|
+
"@media (prefers-reduced-motion: reduce)": "0s",
|
|
326
|
+
},
|
|
319
327
|
transitionTimingFunction: ufTokens.easing,
|
|
320
328
|
outlineWidth: { default: "0", ":focus-visible": "2px" },
|
|
321
329
|
outlineStyle: "solid",
|
|
@@ -325,7 +333,7 @@ const controls = stylex.create({
|
|
|
325
333
|
box: {
|
|
326
334
|
width: ufTokens.sizeControl,
|
|
327
335
|
height: ufTokens.sizeControl,
|
|
328
|
-
borderRadius:
|
|
336
|
+
borderRadius: ufTokens.radiusSm,
|
|
329
337
|
},
|
|
330
338
|
// A switch's track is the one piece of geometry here that is not a token:
|
|
331
339
|
// nothing but a switch reads it, and a theme that changed it would leave the
|
package/props.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @flow
|
|
2
|
+
|
|
3
|
+
import { nativeProps } from "./native-props.js";
|
|
2
4
|
//
|
|
3
5
|
// `@uniflowed/stylex/props`: the merge, and the only part of StyleX that runs.
|
|
4
6
|
//
|
|
@@ -56,7 +58,10 @@ export type CompiledStyle = {
|
|
|
56
58
|
export type StyleArgument = mixed;
|
|
57
59
|
|
|
58
60
|
/** What `props` hands to an element. */
|
|
59
|
-
export type StyleProps = {
|
|
61
|
+
export type StyleProps = {
|
|
62
|
+
readonly className?: string,
|
|
63
|
+
readonly style?: { readonly [string]: string | number },
|
|
64
|
+
};
|
|
60
65
|
|
|
61
66
|
/** What one property contributed, once the merge has picked a winner. */
|
|
62
67
|
type Winner = string | null | CompiledClasses;
|
|
@@ -73,6 +78,8 @@ type Winner = string | null | CompiledClasses;
|
|
|
73
78
|
* survived.
|
|
74
79
|
*/
|
|
75
80
|
export function props(...styles: $ReadOnlyArray<StyleArgument>): StyleProps {
|
|
81
|
+
const native = nativeProps(styles);
|
|
82
|
+
if (native != null) return native;
|
|
76
83
|
const winners: { [string]: Winner } = {};
|
|
77
84
|
collect(styles, winners);
|
|
78
85
|
|
package/theme.js
CHANGED
|
@@ -62,25 +62,23 @@ import { ufTokens } from "./tokens.stylex.js";
|
|
|
62
62
|
* exactly one `@media (prefers-color-scheme: dark)` block.
|
|
63
63
|
*/
|
|
64
64
|
export const ufAutoTheme = stylex.createTheme(ufTokens, {
|
|
65
|
-
canvas: { "@media (prefers-color-scheme: dark)": "#
|
|
66
|
-
sunken: { "@media (prefers-color-scheme: dark)": "#
|
|
67
|
-
surface: { "@media (prefers-color-scheme: dark)": "#
|
|
68
|
-
surfaceHover: { "@media (prefers-color-scheme: dark)": "#
|
|
69
|
-
border: { "@media (prefers-color-scheme: dark)": "#
|
|
70
|
-
ink: { "@media (prefers-color-scheme: dark)": "#
|
|
71
|
-
muted: { "@media (prefers-color-scheme: dark)": "#
|
|
72
|
-
accent: { "@media (prefers-color-scheme: dark)": "#
|
|
73
|
-
accentHover: { "@media (prefers-color-scheme: dark)": "#
|
|
74
|
-
accentInk: { "@media (prefers-color-scheme: dark)": "#
|
|
75
|
-
accentSoft: { "@media (prefers-color-scheme: dark)": "#
|
|
76
|
-
danger: { "@media (prefers-color-scheme: dark)": "#
|
|
77
|
-
dangerHover: { "@media (prefers-color-scheme: dark)": "#
|
|
78
|
-
dangerInk: { "@media (prefers-color-scheme: dark)": "#
|
|
79
|
-
dangerSoft: { "@media (prefers-color-scheme: dark)": "#
|
|
80
|
-
focus: { "@media (prefers-color-scheme: dark)": "#
|
|
81
|
-
scrim: { "@media (prefers-color-scheme: dark)": "rgba(
|
|
82
|
-
shadowCard: { "@media (prefers-color-scheme: dark)": "0 1px 2px rgba(0, 0, 0, 0.40)" },
|
|
83
|
-
shadowPanel: { "@media (prefers-color-scheme: dark)": "0 16px 48px rgba(0, 0, 0, 0.55)" },
|
|
65
|
+
canvas: { "@media (prefers-color-scheme: dark)": "#141413" },
|
|
66
|
+
sunken: { "@media (prefers-color-scheme: dark)": "#0f0f0e" },
|
|
67
|
+
surface: { "@media (prefers-color-scheme: dark)": "#1c1c1a" },
|
|
68
|
+
surfaceHover: { "@media (prefers-color-scheme: dark)": "#262623" },
|
|
69
|
+
border: { "@media (prefers-color-scheme: dark)": "#363632" },
|
|
70
|
+
ink: { "@media (prefers-color-scheme: dark)": "#ecece7" },
|
|
71
|
+
muted: { "@media (prefers-color-scheme: dark)": "#a5a59e" },
|
|
72
|
+
accent: { "@media (prefers-color-scheme: dark)": "#8eaee8" },
|
|
73
|
+
accentHover: { "@media (prefers-color-scheme: dark)": "#a8c1ee" },
|
|
74
|
+
accentInk: { "@media (prefers-color-scheme: dark)": "#101318" },
|
|
75
|
+
accentSoft: { "@media (prefers-color-scheme: dark)": "#1f2838" },
|
|
76
|
+
danger: { "@media (prefers-color-scheme: dark)": "#ee8a80" },
|
|
77
|
+
dangerHover: { "@media (prefers-color-scheme: dark)": "#f3a49c" },
|
|
78
|
+
dangerInk: { "@media (prefers-color-scheme: dark)": "#1d0f0d" },
|
|
79
|
+
dangerSoft: { "@media (prefers-color-scheme: dark)": "#35201d" },
|
|
80
|
+
focus: { "@media (prefers-color-scheme: dark)": "#8eaee8" },
|
|
81
|
+
scrim: { "@media (prefers-color-scheme: dark)": "rgba(0, 0, 0, 0.6)" },
|
|
84
82
|
});
|
|
85
83
|
|
|
86
84
|
/**
|
|
@@ -90,23 +88,21 @@ export const ufAutoTheme = stylex.createTheme(ufTokens, {
|
|
|
90
88
|
* preference a reader chose.
|
|
91
89
|
*/
|
|
92
90
|
export const ufDarkTheme = stylex.createTheme(ufTokens, {
|
|
93
|
-
canvas: "#
|
|
94
|
-
sunken: "#
|
|
95
|
-
surface: "#
|
|
96
|
-
surfaceHover: "#
|
|
97
|
-
border: "#
|
|
98
|
-
ink: "#
|
|
99
|
-
muted: "#
|
|
100
|
-
accent: "#
|
|
101
|
-
accentHover: "#
|
|
102
|
-
accentInk: "#
|
|
103
|
-
accentSoft: "#
|
|
104
|
-
danger: "#
|
|
105
|
-
dangerHover: "#
|
|
106
|
-
dangerInk: "#
|
|
107
|
-
dangerSoft: "#
|
|
108
|
-
focus: "#
|
|
109
|
-
scrim: "rgba(
|
|
110
|
-
shadowCard: "0 1px 2px rgba(0, 0, 0, 0.40)",
|
|
111
|
-
shadowPanel: "0 16px 48px rgba(0, 0, 0, 0.55)",
|
|
91
|
+
canvas: "#141413",
|
|
92
|
+
sunken: "#0f0f0e",
|
|
93
|
+
surface: "#1c1c1a",
|
|
94
|
+
surfaceHover: "#262623",
|
|
95
|
+
border: "#363632",
|
|
96
|
+
ink: "#ecece7",
|
|
97
|
+
muted: "#a5a59e",
|
|
98
|
+
accent: "#8eaee8",
|
|
99
|
+
accentHover: "#a8c1ee",
|
|
100
|
+
accentInk: "#101318",
|
|
101
|
+
accentSoft: "#1f2838",
|
|
102
|
+
danger: "#ee8a80",
|
|
103
|
+
dangerHover: "#f3a49c",
|
|
104
|
+
dangerInk: "#1d0f0d",
|
|
105
|
+
dangerSoft: "#35201d",
|
|
106
|
+
focus: "#8eaee8",
|
|
107
|
+
scrim: "rgba(0, 0, 0, 0.6)",
|
|
112
108
|
});
|
package/tokens.stylex.js
CHANGED
|
@@ -11,18 +11,37 @@
|
|
|
11
11
|
// # Roles, not colours
|
|
12
12
|
//
|
|
13
13
|
// The names are what a token is *for*, never what it looks like. `accent` is
|
|
14
|
-
// the colour a primary action wears; it is
|
|
15
|
-
// anything, and no rule in `preset.js` has to change when it does.
|
|
16
|
-
// named `
|
|
17
|
-
// wanted a green product.
|
|
14
|
+
// the colour a primary action wears; it is a quiet cobalt today and a theme
|
|
15
|
+
// can make it anything, and no rule in `preset.js` has to change when it does.
|
|
16
|
+
// A token named `blue600` would have made the preset unthemeable the moment
|
|
17
|
+
// someone wanted a green product.
|
|
18
18
|
//
|
|
19
|
-
// The
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
19
|
+
// # The palette
|
|
20
|
+
//
|
|
21
|
+
// `docs/ui-visual-language.md` explains it; in short: the interface is
|
|
22
|
+
// neutral first — warm greys with no blue cast, near-black ink on off-white
|
|
23
|
+
// paper — and colour is spent in one place, a single accent for what is
|
|
24
|
+
// selected, current or primary. Danger is a brick red rather than a signal
|
|
25
|
+
// red. Dark is designed as its own palette (charcoal, not navy; a lighter,
|
|
26
|
+
// desaturated accent that does not glow), not the light one inverted.
|
|
27
|
+
//
|
|
28
|
+
// The type and spacing steps are `@uniflowed/brand`'s scales. The colours and
|
|
29
|
+
// the corners are not: brand's cyan-to-magenta spectrum and its round corners
|
|
30
|
+
// are uf's logo and marketing identity, and an interface built from them looks
|
|
31
|
+
// like every other generated one, so this module's values are its own. Brand
|
|
32
|
+
// cannot hold the token module itself anyway — a StyleX token's name is
|
|
33
|
+
// computed by the compiler, and `defineVars` takes literals, not an imported
|
|
34
|
+
// array.
|
|
35
|
+
//
|
|
36
|
+
// # No shadows, no gradients
|
|
37
|
+
//
|
|
38
|
+
// The defaults draw depth with a rule and a change of surface, and nothing
|
|
39
|
+
// else. A shadow is the first thing a theme has to fight when the product's
|
|
40
|
+
// look is flat, and a gradient is a decision about a brand this module does
|
|
41
|
+
// not have, so neither is a token and neither appears in a default style.
|
|
42
|
+
// `crates/uf_stylex/src/tests/defaults.rs` compiles every default style —
|
|
43
|
+
// this module, `preset.js`, `theme.js` and every file in `registry/ui/` — and
|
|
44
|
+
// fails on a `box-shadow`, a `text-shadow`, a `drop-shadow()` or a gradient.
|
|
26
45
|
//
|
|
27
46
|
// # What is not a token
|
|
28
47
|
//
|
|
@@ -45,31 +64,31 @@ import { stylex } from "@uniflowed/stylex";
|
|
|
45
64
|
// `ThemeOverrides<typeof ufTokens>` reject a token this module does not declare.
|
|
46
65
|
export const ufTokens = stylex.defineVars({
|
|
47
66
|
// Surfaces, from furthest back to nearest front.
|
|
48
|
-
canvas: "#
|
|
49
|
-
sunken: "#
|
|
67
|
+
canvas: "#f6f6f4",
|
|
68
|
+
sunken: "#eeeeeb",
|
|
50
69
|
surface: "#ffffff",
|
|
51
|
-
surfaceHover: "#
|
|
52
|
-
border: "#
|
|
70
|
+
surfaceHover: "#f1f1ee",
|
|
71
|
+
border: "#d9d9d4",
|
|
53
72
|
|
|
54
73
|
// Text.
|
|
55
|
-
ink: "#
|
|
56
|
-
muted: "#
|
|
74
|
+
ink: "#1b1b19",
|
|
75
|
+
muted: "#5c5c57",
|
|
57
76
|
|
|
58
77
|
// The colour a primary action wears, and what is legible on it.
|
|
59
|
-
accent: "#
|
|
60
|
-
accentHover: "#
|
|
78
|
+
accent: "#2b58b5",
|
|
79
|
+
accentHover: "#234996",
|
|
61
80
|
accentInk: "#ffffff",
|
|
62
|
-
accentSoft: "#
|
|
81
|
+
accentSoft: "#e8eef9",
|
|
63
82
|
|
|
64
83
|
// The colour a destructive action wears.
|
|
65
|
-
danger: "#
|
|
66
|
-
dangerHover: "#
|
|
84
|
+
danger: "#b1271d",
|
|
85
|
+
dangerHover: "#931f17",
|
|
67
86
|
dangerInk: "#ffffff",
|
|
68
|
-
dangerSoft: "#
|
|
87
|
+
dangerSoft: "#fbedeb",
|
|
69
88
|
|
|
70
89
|
// Focus ring, and the wash behind a modal surface.
|
|
71
|
-
focus: "#
|
|
72
|
-
scrim: "rgba(
|
|
90
|
+
focus: "#2b58b5",
|
|
91
|
+
scrim: "rgba(27, 27, 25, 0.45)",
|
|
73
92
|
|
|
74
93
|
// Type.
|
|
75
94
|
fontSans: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
|
|
@@ -95,20 +114,30 @@ export const ufTokens = stylex.defineVars({
|
|
|
95
114
|
space8: "32px",
|
|
96
115
|
space12: "48px",
|
|
97
116
|
|
|
98
|
-
// Shape.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
// Shape. Three small steps and a pill, and nothing rounder: a control is
|
|
118
|
+
// 2px, a button, field group or popover 4px, and the largest surface — a
|
|
119
|
+
// card, a dialog — 6px. `radiusPill` is for the few things that are round
|
|
120
|
+
// by what they are, such as a switch's track or a radio's dot, and not a
|
|
121
|
+
// way to soften a rectangle.
|
|
122
|
+
radiusSm: "2px",
|
|
123
|
+
radiusMd: "4px",
|
|
124
|
+
radiusLg: "6px",
|
|
103
125
|
radiusPill: "999px",
|
|
104
126
|
|
|
105
127
|
// The size of a control a finger or a pointer aims at.
|
|
106
128
|
sizeControl: "18px",
|
|
107
129
|
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
130
|
+
// Motion. Two short durations and one easing: `durationFast` for a colour
|
|
131
|
+
// or a chevron, `durationBase` for something that travels, such as a
|
|
132
|
+
// switch's thumb or a drawer. The easing decelerates and never overshoots,
|
|
133
|
+
// so nothing bounces. A transition names the properties it moves, never
|
|
134
|
+
// `all`, and is `0s` under `prefers-reduced-motion: reduce`;
|
|
135
|
+
// `crates/uf_stylex/src/tests/defaults.rs` holds every default style to
|
|
136
|
+
// that.
|
|
137
|
+
//
|
|
138
|
+
// There is no elevation either: a surface in front of another is told
|
|
139
|
+
// apart by a 1px `border` and its own background, never by a shadow.
|
|
111
140
|
durationFast: "120ms",
|
|
112
|
-
durationBase: "
|
|
141
|
+
durationBase: "160ms",
|
|
113
142
|
easing: "cubic-bezier(0.2, 0, 0, 1)",
|
|
114
143
|
});
|