@ankhorage/zora 0.16.2 → 1.0.1
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/CHANGELOG.md +74 -0
- package/README.md +27 -22
- package/dist/components/heading/resolveHeadingRecipe.d.ts +2 -2
- package/dist/components/heading/resolveHeadingRecipe.d.ts.map +1 -1
- package/dist/components/heading/resolveHeadingRecipe.js.map +1 -1
- package/dist/components/text/resolveTextRecipe.d.ts +2 -2
- package/dist/components/text/resolveTextRecipe.d.ts.map +1 -1
- package/dist/components/text/resolveTextRecipe.js.map +1 -1
- package/dist/patterns/theme-composer/ThemeComposer.d.ts.map +1 -1
- package/dist/patterns/theme-composer/ThemeComposer.js +101 -86
- package/dist/patterns/theme-composer/ThemeComposer.js.map +1 -1
- package/dist/patterns/theme-composer/index.d.ts +1 -1
- package/dist/patterns/theme-composer/index.d.ts.map +1 -1
- package/dist/patterns/theme-composer/index.js.map +1 -1
- package/dist/patterns/theme-composer/types.d.ts +3 -13
- package/dist/patterns/theme-composer/types.d.ts.map +1 -1
- package/dist/patterns/theme-composer/types.js.map +1 -1
- package/dist/theme/createZoraThemeConfig.d.ts +1 -1
- package/dist/theme/createZoraThemeConfig.d.ts.map +1 -1
- package/dist/theme/createZoraThemeConfig.js +5 -6
- package/dist/theme/createZoraThemeConfig.js.map +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/types.d.ts +16 -11
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js +1 -20
- package/dist/theme/types.js.map +1 -1
- package/dist/theme/useZoraTheme.d.ts +1 -1
- package/dist/theme/zoraDefaultTheme.js +1 -1
- package/dist/theme/zoraDefaultTheme.js.map +1 -1
- package/package.json +4 -4
- package/src/components/heading/resolveHeadingRecipe.test.ts +30 -5
- package/src/components/heading/resolveHeadingRecipe.ts +6 -6
- package/src/components/text/resolveTextRecipe.test.ts +30 -5
- package/src/components/text/resolveTextRecipe.ts +6 -6
- package/src/patterns/theme-composer/ThemeComposer.test.ts +128 -114
- package/src/patterns/theme-composer/ThemeComposer.tsx +130 -128
- package/src/patterns/theme-composer/index.ts +1 -6
- package/src/patterns/theme-composer/types.ts +4 -15
- package/src/showcaseCoverage.test.ts +14 -0
- package/src/theme/createZoraThemeConfig.test.ts +51 -26
- package/src/theme/createZoraThemeConfig.ts +7 -7
- package/src/theme/index.ts +1 -3
- package/src/theme/types.ts +22 -34
- package/src/theme/zoraDefaultTheme.ts +1 -1
- package/dist/internal/color/colorToneRecipes.d.ts +0 -23
- package/dist/internal/color/colorToneRecipes.d.ts.map +0 -1
- package/dist/internal/color/colorToneRecipes.js +0 -139
- package/dist/internal/color/colorToneRecipes.js.map +0 -1
- package/dist/internal/color/harmony.d.ts +0 -12
- package/dist/internal/color/harmony.d.ts.map +0 -1
- package/dist/internal/color/harmony.js +0 -69
- package/dist/internal/color/harmony.js.map +0 -1
- package/dist/internal/color/hue.d.ts +0 -3
- package/dist/internal/color/hue.d.ts.map +0 -1
- package/dist/internal/color/hue.js +0 -7
- package/dist/internal/color/hue.js.map +0 -1
- package/dist/internal/color/index.d.ts +0 -10
- package/dist/internal/color/index.d.ts.map +0 -1
- package/dist/internal/color/index.js +0 -10
- package/dist/internal/color/index.js.map +0 -1
- package/dist/internal/color/oklch.d.ts +0 -6
- package/dist/internal/color/oklch.d.ts.map +0 -1
- package/dist/internal/color/oklch.js +0 -50
- package/dist/internal/color/oklch.js.map +0 -1
- package/dist/internal/color/primary.d.ts +0 -3
- package/dist/internal/color/primary.d.ts.map +0 -1
- package/dist/internal/color/primary.js +0 -44
- package/dist/internal/color/primary.js.map +0 -1
- package/dist/internal/color/roleHues.d.ts +0 -15
- package/dist/internal/color/roleHues.d.ts.map +0 -1
- package/dist/internal/color/roleHues.js +0 -103
- package/dist/internal/color/roleHues.js.map +0 -1
- package/dist/internal/color/roleScales.d.ts +0 -20
- package/dist/internal/color/roleScales.d.ts.map +0 -1
- package/dist/internal/color/roleScales.js +0 -79
- package/dist/internal/color/roleScales.js.map +0 -1
- package/dist/internal/color/scales.d.ts +0 -19
- package/dist/internal/color/scales.d.ts.map +0 -1
- package/dist/internal/color/scales.js +0 -135
- package/dist/internal/color/scales.js.map +0 -1
- package/dist/internal/color/semanticTokens.d.ts +0 -28
- package/dist/internal/color/semanticTokens.d.ts.map +0 -1
- package/dist/internal/color/semanticTokens.js +0 -84
- package/dist/internal/color/semanticTokens.js.map +0 -1
- package/dist/internal/color/types.d.ts +0 -10
- package/dist/internal/color/types.d.ts.map +0 -1
- package/dist/internal/color/types.js +0 -4
- package/dist/internal/color/types.js.map +0 -1
- package/dist/patterns/theme-composer/recommendations.d.ts +0 -14
- package/dist/patterns/theme-composer/recommendations.d.ts.map +0 -1
- package/dist/patterns/theme-composer/recommendations.js +0 -58
- package/dist/patterns/theme-composer/recommendations.js.map +0 -1
- package/src/internal/color/colorToneRecipes.test.ts +0 -89
- package/src/internal/color/colorToneRecipes.ts +0 -167
- package/src/internal/color/harmony.test.ts +0 -145
- package/src/internal/color/harmony.ts +0 -96
- package/src/internal/color/hue.test.ts +0 -28
- package/src/internal/color/hue.ts +0 -7
- package/src/internal/color/index.ts +0 -44
- package/src/internal/color/oklch.ts +0 -65
- package/src/internal/color/primary.test.ts +0 -105
- package/src/internal/color/primary.ts +0 -64
- package/src/internal/color/roleHues.test.ts +0 -197
- package/src/internal/color/roleHues.ts +0 -142
- package/src/internal/color/roleScales.test.ts +0 -220
- package/src/internal/color/roleScales.ts +0 -127
- package/src/internal/color/scales.test.ts +0 -151
- package/src/internal/color/scales.ts +0 -194
- package/src/internal/color/semanticTokens.test.ts +0 -170
- package/src/internal/color/semanticTokens.ts +0 -114
- package/src/internal/color/types.ts +0 -15
- package/src/patterns/theme-composer/recommendations.ts +0 -85
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 938bcfe: ThemeComposer now edits the full ZoraTheme source model.
|
|
8
|
+
- ThemeComposer adds name editing with empty-name validation.
|
|
9
|
+
- ThemeComposer adds app category editing via a Select using APP_CATEGORIES from @ankhorage/contracts.
|
|
10
|
+
- ThemeComposer supports optional `appCategories` prop for narrowing the category options list.
|
|
11
|
+
- ThemeComposer validates primary color input with parseHexColorOrThrow from @ankhorage/color-theory while keeping public `primaryColor` as `string`.
|
|
12
|
+
- ThemeComposer preview shows name, appCategory, primaryColor, and harmony metadata.
|
|
13
|
+
- README and examples app updated to reflect the new API.
|
|
14
|
+
|
|
15
|
+
## 1.0.0
|
|
16
|
+
|
|
17
|
+
### Major Changes
|
|
18
|
+
|
|
19
|
+
- 4d50ada: **Breaking: ZORA core theme model and color stack cleanup**
|
|
20
|
+
|
|
21
|
+
## Removed APIs
|
|
22
|
+
- `ZoraTheme.colorTone` — removed; `colorTone` is no longer part of the theme seed
|
|
23
|
+
- `ZoraColorTone` type — removed
|
|
24
|
+
- `ZORA_COLOR_TONES` constant — removed
|
|
25
|
+
- `ZoraHexColor` type — removed; ZORA themes now accept normal string hex values
|
|
26
|
+
- `ZoraComputedTheme.mode` — replaced by `light` and `dark` mode objects
|
|
27
|
+
- `ThemeComposerRecommendation` — removed
|
|
28
|
+
- `ThemeComposerAppMood` — removed
|
|
29
|
+
- `ThemeComposerAppCategory` — removed (was an opaque `string` alias)
|
|
30
|
+
- `ThemeComposerProps.appMood` — removed
|
|
31
|
+
- `ThemeComposerProps.recommendations` — removed
|
|
32
|
+
- Internal color stack (`src/internal/color/`) — removed; ZORA no longer owns color math
|
|
33
|
+
|
|
34
|
+
## Added / Changed APIs
|
|
35
|
+
- `ZoraTheme.name` — now **required** (was optional); ZORA themes must provide a real display name
|
|
36
|
+
- `ZoraTheme.appCategory` — new required field; use `AppCategory` from `@ankhorage/contracts`
|
|
37
|
+
- `ZoraTheme.primaryColor` — remains a public `string`; ZORA validates it internally with `@ankhorage/color-theory`
|
|
38
|
+
- `ZoraComputedTheme` — now has `light: ZoraComputedThemeMode` and `dark: ZoraComputedThemeMode` instead of a single `mode`
|
|
39
|
+
- `ZoraComputedThemeMode` — new type: `{ mode, surfaceTheme, generated, swatches, semanticColors? }`
|
|
40
|
+
- Primary color is now preserved identically for both light and dark `ThemeConfig` modes (no dark-mode mutation)
|
|
41
|
+
- `SurfaceTheme` (from `@ankhorage/surface`) replaces `AnkhTheme` as the resolved runtime theme type
|
|
42
|
+
|
|
43
|
+
## New dependencies
|
|
44
|
+
- `@ankhorage/color-theory@^0.0.2` — canonical color types and generation utilities
|
|
45
|
+
- `@ankhorage/contracts@^1.1.0` — theme config and app category types
|
|
46
|
+
|
|
47
|
+
## Removed dependencies
|
|
48
|
+
- `culori` — no longer a direct ZORA dependency; color math is delegated to `@ankhorage/color-theory`
|
|
49
|
+
- `@types/culori` — removed
|
|
50
|
+
|
|
51
|
+
## Migration
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// Before
|
|
55
|
+
const theme: ZoraTheme = {
|
|
56
|
+
id: 'my-theme',
|
|
57
|
+
name: 'My Theme',
|
|
58
|
+
primaryColor: '#0f766e',
|
|
59
|
+
harmony: 'analogous',
|
|
60
|
+
colorTone: 'jewel',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// After
|
|
64
|
+
const theme: ZoraTheme = {
|
|
65
|
+
id: 'my-theme',
|
|
66
|
+
name: 'My Theme',
|
|
67
|
+
appCategory: 'developer_tools',
|
|
68
|
+
primaryColor: '#0f766e',
|
|
69
|
+
harmony: 'analogous',
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `name` field is now required. `primaryColor` stays app-facing and ergonomic as
|
|
74
|
+
a string, while ZORA validates it when converting the source theme to a runtime
|
|
75
|
+
`ThemeConfig`.
|
|
76
|
+
|
|
3
77
|
## 0.16.2
|
|
4
78
|
|
|
5
79
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -90,28 +90,24 @@ export function App({ appTheme }: { appTheme: ZoraTheme }) {
|
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
ZORA themes use a single seed `primaryColor`.
|
|
94
|
-
|
|
93
|
+
ZORA themes use a single seed `primaryColor`. The selected primary color is preserved
|
|
94
|
+
identically for both light and dark mode. Color generation is handled by
|
|
95
|
+
`@ankhorage/color-theory` via Surface.
|
|
95
96
|
|
|
96
97
|
```tsx
|
|
97
98
|
<ZoraProvider
|
|
98
99
|
theme={{
|
|
99
100
|
id: 'studio',
|
|
101
|
+
name: 'Studio',
|
|
102
|
+
appCategory: 'developer_tools',
|
|
100
103
|
primaryColor: '#0f766e',
|
|
101
104
|
harmony: 'analogous',
|
|
102
|
-
colorTone: 'jewel',
|
|
103
105
|
}}
|
|
104
106
|
>
|
|
105
107
|
<App />
|
|
106
108
|
</ZoraProvider>
|
|
107
109
|
```
|
|
108
110
|
|
|
109
|
-
> **`colorTone` vs component `tone`** — `colorTone` is a theme-seed field that
|
|
110
|
-
> selects the color-world / palette tone for the whole theme (e.g. `'jewel'`,
|
|
111
|
-
> `'pastel'`). Component `tone` props (e.g. `<Text tone="muted" />`,
|
|
112
|
-
> `<Button tone="primary" />`) express semantic color intent inside that theme
|
|
113
|
-
> and are independent of `colorTone`.
|
|
114
|
-
|
|
115
111
|
`mode` and `themeId` are available on public ZORA components through `ZoraBaseProps`.
|
|
116
112
|
Use component props for local component/subtree overrides.
|
|
117
113
|
|
|
@@ -1372,7 +1368,13 @@ Pass your current theme as `value` and handle updates through `onChange`. Wrap b
|
|
|
1372
1368
|
in a `ZoraProvider` so the preview area reflects every change immediately.
|
|
1373
1369
|
|
|
1374
1370
|
```tsx
|
|
1375
|
-
const [theme, setTheme] = React.useState<ZoraTheme>(
|
|
1371
|
+
const [theme, setTheme] = React.useState<ZoraTheme>({
|
|
1372
|
+
id: 'my-app',
|
|
1373
|
+
name: 'My App',
|
|
1374
|
+
appCategory: 'developer_tools',
|
|
1375
|
+
primaryColor: '#0f766e',
|
|
1376
|
+
harmony: 'analogous',
|
|
1377
|
+
});
|
|
1376
1378
|
const [mode, setMode] = React.useState<ZoraThemeMode>('light');
|
|
1377
1379
|
|
|
1378
1380
|
return (
|
|
@@ -1393,14 +1395,15 @@ return (
|
|
|
1393
1395
|
|
|
1394
1396
|
ZORA props:
|
|
1395
1397
|
|
|
1396
|
-
| Prop
|
|
1397
|
-
|
|
|
1398
|
-
| `value`
|
|
1399
|
-
| `onChange`
|
|
1400
|
-
| `mode`
|
|
1401
|
-
| `onModeChange`
|
|
1402
|
-
| `onSubmit`
|
|
1403
|
-
| `
|
|
1398
|
+
| Prop | Type | Default | Notes |
|
|
1399
|
+
| --------------- | ------------------------------- | ---------------- | ---------------------------------------------------- |
|
|
1400
|
+
| `value` | `ZoraTheme` | - | Required controlled theme seed. |
|
|
1401
|
+
| `onChange` | `(theme: ZoraTheme) => void` | - | Required. Fires on every valid change. |
|
|
1402
|
+
| `mode` | `ZoraThemeMode` | - | Current light/dark mode shown in the mode toggle. |
|
|
1403
|
+
| `onModeChange` | `(mode: ZoraThemeMode) => void` | - | Called when the user switches the mode toggle. |
|
|
1404
|
+
| `onSubmit` | `(theme: ZoraTheme) => void` | - | Optional. Renders an "Apply theme" button if set. |
|
|
1405
|
+
| `appCategories` | `readonly AppCategory[]` | `APP_CATEGORIES` | Optional override for the app category options list. |
|
|
1406
|
+
| `testID` | `string` | - | Forwarded to the root element and child test points. |
|
|
1404
1407
|
|
|
1405
1408
|
</details>
|
|
1406
1409
|
|
|
@@ -1425,9 +1428,9 @@ Pass a theme seed to define your app theme:
|
|
|
1425
1428
|
theme={{
|
|
1426
1429
|
id: 'studio',
|
|
1427
1430
|
name: 'Studio',
|
|
1431
|
+
appCategory: 'developer_tools',
|
|
1428
1432
|
primaryColor: '#0f766e',
|
|
1429
1433
|
harmony: 'analogous',
|
|
1430
|
-
colorTone: 'jewel',
|
|
1431
1434
|
}}
|
|
1432
1435
|
>
|
|
1433
1436
|
<App />
|
|
@@ -1453,15 +1456,17 @@ No inherited props. `ZoraProviderProps` is declared directly by ZORA.
|
|
|
1453
1456
|
|
|
1454
1457
|
### `createZoraThemeConfig`
|
|
1455
1458
|
|
|
1456
|
-
Creates a
|
|
1459
|
+
Creates a `ThemeConfig` (from `@ankhorage/contracts`) from a ZORA theme seed.
|
|
1460
|
+
The primary color is preserved identically for both light and dark mode configs.
|
|
1461
|
+
Color generation is handled downstream by `@ankhorage/color-theory` via Surface.
|
|
1457
1462
|
|
|
1458
1463
|
```tsx
|
|
1459
1464
|
const themeConfig = createZoraThemeConfig({
|
|
1460
1465
|
id: 'studio',
|
|
1461
1466
|
name: 'Studio',
|
|
1467
|
+
appCategory: 'developer_tools',
|
|
1462
1468
|
primaryColor: '#0f766e',
|
|
1463
1469
|
harmony: 'analogous',
|
|
1464
|
-
colorTone: 'jewel',
|
|
1465
1470
|
});
|
|
1466
1471
|
```
|
|
1467
1472
|
|
|
@@ -1485,9 +1490,9 @@ Default ZORA theme seed.
|
|
|
1485
1490
|
const zoraDefaultTheme: ZoraTheme = {
|
|
1486
1491
|
id: 'zora',
|
|
1487
1492
|
name: 'ZORA',
|
|
1493
|
+
appCategory: 'developer_tools',
|
|
1488
1494
|
primaryColor: '#0f766e',
|
|
1489
1495
|
harmony: 'analogous',
|
|
1490
|
-
colorTone: 'jewel',
|
|
1491
1496
|
};
|
|
1492
1497
|
```
|
|
1493
1498
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SurfaceTheme } from '@ankhorage/surface';
|
|
2
2
|
import type { TextStyle } from 'react-native';
|
|
3
3
|
import type { HeadingAlign, HeadingLevel, HeadingSize, HeadingTone, HeadingWeight } from './types';
|
|
4
4
|
interface ResolveHeadingRecipeOptions {
|
|
@@ -10,6 +10,6 @@ interface ResolveHeadingRecipeOptions {
|
|
|
10
10
|
italic?: boolean;
|
|
11
11
|
}
|
|
12
12
|
export declare function resolveHeadingSizeFromLevel(level: HeadingLevel): HeadingSize;
|
|
13
|
-
export declare function resolveHeadingRecipe(theme:
|
|
13
|
+
export declare function resolveHeadingRecipe(theme: SurfaceTheme, { align, italic, level, size, tone, weight }: ResolveHeadingRecipeOptions): TextStyle;
|
|
14
14
|
export {};
|
|
15
15
|
//# sourceMappingURL=resolveHeadingRecipe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveHeadingRecipe.d.ts","sourceRoot":"","sources":["../../../src/components/heading/resolveHeadingRecipe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"resolveHeadingRecipe.d.ts","sourceRoot":"","sources":["../../../src/components/heading/resolveHeadingRecipe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEnG,UAAU,2BAA2B;IACnC,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAQD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAe5E;AA6ED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,EAAE,KAAK,EAAE,MAAc,EAAE,KAAK,EAAE,IAAI,EAAE,IAAgB,EAAE,MAAM,EAAE,EAAE,2BAA2B,GAC5F,SAAS,CAcX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveHeadingRecipe.js","sourceRoot":"","sources":["../../../src/components/heading/resolveHeadingRecipe.ts"],"names":[],"mappings":"AAoBA,MAAM,UAAU,2BAA2B,CAAC,KAAmB;IAC7D,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,CAAC;YACJ,OAAO,IAAI,CAAC;QACd,KAAK,CAAC;YACJ,OAAO,IAAI,CAAC;QACd,KAAK,CAAC;YACJ,OAAO,IAAI,CAAC;QACd,KAAK,CAAC;YACJ,OAAO,IAAI,CAAC;QACd,KAAK,CAAC;YACJ,OAAO,IAAI,CAAC;QACd,KAAK,CAAC;YACJ,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAS,2BAA2B,CAAC,IAAqC;IACxE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,IAAI;YACP,OAAO,CAAC,CAAC;QACX,KAAK,IAAI;YACP,OAAO,CAAC,CAAC;QACX,KAAK,IAAI;YACP,OAAO,CAAC,CAAC;QACX,KAAK,IAAI;YACP,OAAO,CAAC,CAAC;QACX,KAAK,IAAI;YACP,OAAO,CAAC,CAAC;QACX,KAAK,IAAI;YACP,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"resolveHeadingRecipe.js","sourceRoot":"","sources":["../../../src/components/heading/resolveHeadingRecipe.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FontWeight, SurfaceTheme } from '@ankhorage/surface';\nimport type { TextStyle } from 'react-native';\n\nimport type { HeadingAlign, HeadingLevel, HeadingSize, HeadingTone, HeadingWeight } from './types';\n\ninterface ResolveHeadingRecipeOptions {\n level: HeadingLevel;\n size?: HeadingSize;\n tone?: HeadingTone;\n align?: HeadingAlign;\n weight?: HeadingWeight;\n italic?: boolean;\n}\n\ninterface HeadingRecipe {\n fontSize: number;\n lineHeight: number;\n weight: HeadingWeight;\n}\n\nexport function resolveHeadingSizeFromLevel(level: HeadingLevel): HeadingSize {\n switch (level) {\n case 1:\n return 'h1';\n case 2:\n return 'h2';\n case 3:\n return 'h3';\n case 4:\n return 'h4';\n case 5:\n return 'h5';\n case 6:\n return 'h6';\n }\n}\n\nfunction resolveHeadingLevelFromSize(size: Exclude<HeadingSize, 'display'>): HeadingLevel {\n switch (size) {\n case 'h1':\n return 1;\n case 'h2':\n return 2;\n case 'h3':\n return 3;\n case 'h4':\n return 4;\n case 'h5':\n return 5;\n case 'h6':\n return 6;\n }\n}\n\nfunction resolveSizeRecipe(theme: SurfaceTheme, size: HeadingSize): HeadingRecipe {\n if (size === 'display') {\n const fontSize = theme.typography.sizes['3xl'];\n\n return {\n fontSize,\n lineHeight: fontSize + 8,\n weight: 'bold',\n };\n }\n\n const heading = theme.typography.headings[resolveHeadingLevelFromSize(size)];\n\n return {\n fontSize: heading.size,\n lineHeight: heading.lineHeight,\n weight: heading.weight,\n };\n}\n\nfunction resolveToneColor(theme: SurfaceTheme, tone: HeadingTone): string {\n switch (tone) {\n case 'muted':\n return theme.semantics.content.muted;\n case 'subtle':\n return theme.semantics.content.subtle;\n case 'inverse':\n return theme.semantics.content.inverse;\n case 'primary':\n return theme.semantics.brand.base;\n case 'danger':\n return theme.semantics.danger.base;\n case 'success':\n return theme.semantics.success.base;\n case 'warning':\n return theme.semantics.warning.base;\n case 'default':\n default:\n return theme.semantics.content.default;\n }\n}\n\nfunction resolveWeight(theme: SurfaceTheme, weight: HeadingWeight): FontWeight {\n return theme.typography.weights[weight];\n}\n\nfunction resolveFontFamily({\n theme,\n weight,\n italic,\n}: {\n theme: SurfaceTheme;\n weight: FontWeight;\n italic: boolean;\n}): string | undefined {\n return theme.typography.fonts[italic ? 'italic' : 'normal'][weight];\n}\n\nexport function resolveHeadingRecipe(\n theme: SurfaceTheme,\n { align, italic = false, level, size, tone = 'default', weight }: ResolveHeadingRecipeOptions,\n): TextStyle {\n const recipe = resolveSizeRecipe(theme, size ?? resolveHeadingSizeFromLevel(level));\n const resolvedWeight = resolveWeight(theme, weight ?? recipe.weight);\n\n return {\n color: resolveToneColor(theme, tone),\n elevation: 0,\n fontFamily: resolveFontFamily({ theme, weight: resolvedWeight, italic }),\n fontSize: recipe.fontSize,\n fontStyle: italic ? 'italic' : 'normal',\n fontWeight: resolvedWeight,\n lineHeight: recipe.lineHeight,\n textAlign: align,\n };\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Breakpoint, type Responsive, type SurfaceTheme } from '@ankhorage/surface';
|
|
2
2
|
import type { TextStyle } from 'react-native';
|
|
3
3
|
import type { TextAlign, TextTone, TextVariant, TextWeight } from './types';
|
|
4
4
|
interface ResolveTextStyleOptions {
|
|
5
|
-
theme:
|
|
5
|
+
theme: SurfaceTheme;
|
|
6
6
|
breakpoint: Breakpoint;
|
|
7
7
|
variant?: Responsive<TextVariant>;
|
|
8
8
|
tone?: Responsive<TextTone>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveTextRecipe.d.ts","sourceRoot":"","sources":["../../../src/components/text/resolveTextRecipe.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,
|
|
1
|
+
{"version":3,"file":"resolveTextRecipe.d.ts","sourceRoot":"","sources":["../../../src/components/text/resolveTextRecipe.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAU5E,UAAU,uBAAuB;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA0GD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,UAAU,EACV,OAAO,EACP,IAAI,EACJ,MAAM,EACN,KAAK,EACL,MAAc,GACf,EAAE,uBAAuB,GAAG,SAAS,CA2BrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveTextRecipe.js","sourceRoot":"","sources":["../../../src/components/text/resolveTextRecipe.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"resolveTextRecipe.js","sourceRoot":"","sources":["../../../src/components/text/resolveTextRecipe.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Breakpoint,\n type FontWeight,\n resolveResponsive,\n type Responsive,\n type SurfaceTheme,\n} from '@ankhorage/surface';\nimport type { TextStyle } from 'react-native';\n\nimport type { TextAlign, TextTone, TextVariant, TextWeight } from './types';\n\ninterface VariantRecipe {\n fontSize: number;\n lineHeight: number;\n weight: TextWeight;\n textTransform?: TextStyle['textTransform'];\n letterSpacing?: number;\n}\n\ninterface ResolveTextStyleOptions {\n theme: SurfaceTheme;\n breakpoint: Breakpoint;\n variant?: Responsive<TextVariant>;\n tone?: Responsive<TextTone>;\n weight?: Responsive<TextWeight>;\n align?: Responsive<TextAlign>;\n italic?: boolean;\n}\n\nfunction isMediumBreakpointOrLarger(breakpoint: Breakpoint): boolean {\n return breakpoint === 'md' || breakpoint === 'lg' || breakpoint === 'xl';\n}\n\nfunction resolveWeight(theme: SurfaceTheme, weight: TextWeight): FontWeight {\n return theme.typography.weights[weight];\n}\n\nfunction resolveFontFamily({\n theme,\n variant,\n weight,\n italic,\n}: {\n theme: SurfaceTheme;\n variant: TextVariant;\n weight: FontWeight;\n italic: boolean;\n}): string | undefined {\n if (variant === 'code') {\n return 'monospace';\n }\n\n return theme.typography.fonts[italic ? 'italic' : 'normal'][weight];\n}\n\nfunction resolveVariantRecipe(\n theme: SurfaceTheme,\n variant: TextVariant,\n breakpoint: Breakpoint,\n): VariantRecipe {\n switch (variant) {\n case 'lead':\n return {\n fontSize: isMediumBreakpointOrLarger(breakpoint)\n ? theme.typography.sizes.l\n : theme.typography.sizes.m,\n lineHeight: isMediumBreakpointOrLarger(breakpoint) ? 28 : 24,\n weight: 'regular',\n };\n case 'bodySmall':\n return {\n fontSize: theme.typography.sizes.s,\n lineHeight: 20,\n weight: 'regular',\n };\n case 'caption':\n return {\n fontSize: theme.typography.sizes.xs,\n lineHeight: 16,\n weight: 'regular',\n };\n case 'label':\n return {\n fontSize: theme.typography.sizes.s,\n lineHeight: 18,\n weight: 'medium',\n };\n case 'eyebrow':\n return {\n fontSize: theme.typography.sizes.xs,\n lineHeight: 16,\n weight: 'semiBold',\n textTransform: 'uppercase',\n letterSpacing: 0.8,\n };\n case 'code':\n return {\n fontSize: theme.typography.sizes.s,\n lineHeight: 20,\n weight: 'regular',\n };\n case 'body':\n default:\n return {\n fontSize: theme.typography.sizes.m,\n lineHeight: 24,\n weight: 'regular',\n };\n }\n}\n\nfunction resolveToneColor(theme: SurfaceTheme, tone: TextTone): string {\n switch (tone) {\n case 'muted':\n return theme.semantics.content.muted;\n case 'subtle':\n return theme.semantics.content.subtle;\n case 'inverse':\n return theme.semantics.content.inverse;\n case 'primary':\n return theme.semantics.brand.base;\n case 'danger':\n return theme.semantics.danger.base;\n case 'success':\n return theme.semantics.success.base;\n case 'warning':\n return theme.semantics.warning.base;\n case 'default':\n default:\n return theme.semantics.content.default;\n }\n}\n\nexport function resolveTextStyle({\n theme,\n breakpoint,\n variant,\n tone,\n weight,\n align,\n italic = false,\n}: ResolveTextStyleOptions): TextStyle {\n const resolvedVariant = resolveResponsive(variant, breakpoint) ?? 'body';\n const resolvedTone = resolveResponsive(tone, breakpoint) ?? 'default';\n const resolvedAlign = resolveResponsive(align, breakpoint);\n const recipe = resolveVariantRecipe(theme, resolvedVariant, breakpoint);\n const resolvedWeight = resolveWeight(\n theme,\n resolveResponsive(weight, breakpoint) ?? recipe.weight,\n );\n\n return {\n color: resolveToneColor(theme, resolvedTone),\n elevation: 0,\n fontFamily: resolveFontFamily({\n theme,\n variant: resolvedVariant,\n weight: resolvedWeight,\n italic,\n }),\n fontSize: recipe.fontSize,\n fontStyle: italic ? 'italic' : 'normal',\n fontWeight: resolvedWeight,\n letterSpacing: recipe.letterSpacing,\n lineHeight: recipe.lineHeight,\n textAlign: resolvedAlign,\n textTransform: recipe.textTransform,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeComposer.d.ts","sourceRoot":"","sources":["../../../src/patterns/theme-composer/ThemeComposer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ThemeComposer.d.ts","sourceRoot":"","sources":["../../../src/patterns/theme-composer/ThemeComposer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAa1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAmSlD,eAAO,MAAM,aAAa,0DAAyC,CAAC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { COLOR_HARMONIES, parseHexColorOrThrow } from '@ankhorage/color-theory';
|
|
2
|
+
import { APP_CATEGORIES } from '@ankhorage/contracts';
|
|
1
3
|
import { Box, Stack } from '@ankhorage/surface';
|
|
2
4
|
import React from 'react';
|
|
3
5
|
import { Badge } from '../../components/badge';
|
|
@@ -8,53 +10,55 @@ import { Input } from '../../components/input';
|
|
|
8
10
|
import { Select } from '../../components/select';
|
|
9
11
|
import { Tabs } from '../../components/tabs';
|
|
10
12
|
import { Text } from '../../components/text';
|
|
11
|
-
import { ZORA_COLOR_HARMONIES, ZORA_COLOR_TONES, } from '../../theme/types';
|
|
12
13
|
import { useZoraTheme } from '../../theme/useZoraTheme';
|
|
13
14
|
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
14
|
-
|
|
15
|
-
const
|
|
15
|
+
const HEX_ERROR_MESSAGE = 'Enter a valid 6-digit hex color (e.g. #0f766e).';
|
|
16
|
+
const NAME_ERROR_MESSAGE = 'Theme name cannot be empty.';
|
|
16
17
|
function isValidHex(value) {
|
|
17
|
-
|
|
18
|
+
try {
|
|
19
|
+
parseHexColorOrThrow(value);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function formatAppCategoryLabel(category) {
|
|
27
|
+
return category
|
|
28
|
+
.split('_')
|
|
29
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
30
|
+
.join(' ');
|
|
18
31
|
}
|
|
19
|
-
const HARMONY_OPTIONS =
|
|
20
|
-
const TONE_OPTIONS = ZORA_COLOR_TONES.map((t) => ({ value: t, label: t }));
|
|
32
|
+
const HARMONY_OPTIONS = COLOR_HARMONIES.map((h) => ({ value: h, label: h }));
|
|
21
33
|
const MODE_TABS = [
|
|
22
34
|
{ value: 'light', label: 'Light' },
|
|
23
35
|
{ value: 'dark', label: 'Dark' },
|
|
24
36
|
];
|
|
25
|
-
|
|
26
|
-
neutral: 'Neutral',
|
|
27
|
-
pastel: 'Pastel',
|
|
28
|
-
earth: 'Earth',
|
|
29
|
-
mineral: 'Mineral',
|
|
30
|
-
muted: 'Muted',
|
|
31
|
-
jewel: 'Neutral',
|
|
32
|
-
fluorescent: 'Obsidian',
|
|
33
|
-
obsidian: 'Obsidian',
|
|
34
|
-
vaporwave: 'Pastel',
|
|
35
|
-
monochromeAccent: 'Neutral',
|
|
36
|
-
};
|
|
37
|
-
const COLOR_TONE_FOREGROUND_TONE = {
|
|
38
|
-
neutral: 'Jewel',
|
|
39
|
-
pastel: 'Jewel',
|
|
40
|
-
earth: 'Mineral',
|
|
41
|
-
mineral: 'Jewel',
|
|
42
|
-
muted: 'Jewel',
|
|
43
|
-
jewel: 'Jewel',
|
|
44
|
-
fluorescent: 'Fluorescent',
|
|
45
|
-
obsidian: 'Fluorescent',
|
|
46
|
-
vaporwave: 'Fluorescent',
|
|
47
|
-
monochromeAccent: 'Jewel',
|
|
48
|
-
};
|
|
49
|
-
function ThemeComposerInner({ themeId: _themeId, value, onChange, mode, onModeChange, onSubmit, appCategory, appMood, recommendations, testID, }) {
|
|
37
|
+
function ThemeComposerInner({ themeId: _themeId, value, onChange, mode, onModeChange, onSubmit, appCategories, testID, }) {
|
|
50
38
|
const { theme } = useZoraTheme();
|
|
51
39
|
const [hexInput, setHexInput] = React.useState(value.primaryColor);
|
|
52
40
|
const [hexError, setHexError] = React.useState(undefined);
|
|
53
|
-
|
|
41
|
+
const [nameInput, setNameInput] = React.useState(value.name);
|
|
42
|
+
const [nameError, setNameError] = React.useState(undefined);
|
|
43
|
+
// Keep local inputs in sync when value changes externally
|
|
54
44
|
React.useEffect(() => {
|
|
55
45
|
setHexInput(value.primaryColor);
|
|
56
46
|
setHexError(undefined);
|
|
57
47
|
}, [value.primaryColor]);
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
setNameInput(value.name);
|
|
50
|
+
setNameError(undefined);
|
|
51
|
+
}, [value.name]);
|
|
52
|
+
function handleNameChange(text) {
|
|
53
|
+
setNameInput(text);
|
|
54
|
+
if (text.trim().length === 0) {
|
|
55
|
+
setNameError(NAME_ERROR_MESSAGE);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
setNameError(undefined);
|
|
59
|
+
onChange({ ...value, name: text });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
58
62
|
function handleHexChange(text) {
|
|
59
63
|
// Ensure leading hash
|
|
60
64
|
const normalized = text.startsWith('#') ? text : `#${text}`;
|
|
@@ -64,44 +68,56 @@ function ThemeComposerInner({ themeId: _themeId, value, onChange, mode, onModeCh
|
|
|
64
68
|
onChange({ ...value, primaryColor: normalized });
|
|
65
69
|
}
|
|
66
70
|
else {
|
|
67
|
-
setHexError(
|
|
71
|
+
setHexError(HEX_ERROR_MESSAGE);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function handleSubmit() {
|
|
75
|
+
const hasValidName = nameInput.trim().length > 0;
|
|
76
|
+
const hasValidHex = isValidHex(hexInput);
|
|
77
|
+
if (!hasValidName) {
|
|
78
|
+
setNameError(NAME_ERROR_MESSAGE);
|
|
79
|
+
}
|
|
80
|
+
if (!hasValidHex) {
|
|
81
|
+
setHexError(HEX_ERROR_MESSAGE);
|
|
82
|
+
}
|
|
83
|
+
if (!hasValidName || !hasValidHex) {
|
|
84
|
+
return;
|
|
68
85
|
}
|
|
86
|
+
onSubmit?.({
|
|
87
|
+
...value,
|
|
88
|
+
name: nameInput,
|
|
89
|
+
primaryColor: hexInput,
|
|
90
|
+
});
|
|
69
91
|
}
|
|
70
92
|
const activeMode = mode ?? 'light';
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
});
|
|
93
|
+
const categoryOptions = (appCategories ?? APP_CATEGORIES).map((c) => ({
|
|
94
|
+
value: c,
|
|
95
|
+
label: formatAppCategoryLabel(c),
|
|
96
|
+
}));
|
|
76
97
|
return (<Stack gap="l" testID={testID}>
|
|
77
|
-
{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
<Badge tone="neutral" emphasis="soft">
|
|
92
|
-
{formatThemeComposerLabel(recommendation.suggestedHarmony)} harmony
|
|
93
|
-
</Badge>
|
|
94
|
-
{recommendation.suggestedPrimaryHueDegrees === undefined ? null : (<Badge tone="neutral" emphasis="soft">
|
|
95
|
-
{recommendation.suggestedPrimaryHueDegrees}° hue
|
|
96
|
-
</Badge>)}
|
|
97
|
-
</Stack>
|
|
98
|
-
<Text tone="muted" variant="bodySmall">
|
|
99
|
-
Suggested for {formatThemeComposerLabel(recommendation.appCategory)}. The color tone
|
|
100
|
-
controls palette character, harmony controls accent relationships, and hue sets the
|
|
101
|
-
starting primary color when available.
|
|
98
|
+
{/* Section: Theme identity */}
|
|
99
|
+
<Card title="Theme identity" description="Name your theme. The ID is assigned automatically and shown for reference.">
|
|
100
|
+
<Stack gap="m">
|
|
101
|
+
<Stack gap="xs">
|
|
102
|
+
<Text variant="label">Name</Text>
|
|
103
|
+
<Input value={nameInput} onChangeText={handleNameChange} placeholder="My theme" autoCorrect={false} invalid={nameError !== undefined} testID={testID ? `${testID}-name-input` : undefined}/>
|
|
104
|
+
{nameError ? (<Text tone="danger" variant="bodySmall">
|
|
105
|
+
{nameError}
|
|
106
|
+
</Text>) : null}
|
|
107
|
+
</Stack>
|
|
108
|
+
<Stack gap="xs">
|
|
109
|
+
<Text variant="label">ID</Text>
|
|
110
|
+
<Text tone="muted" variant="bodySmall" testID={testID ? `${testID}-id-display` : undefined}>
|
|
111
|
+
{value.id}
|
|
102
112
|
</Text>
|
|
103
113
|
</Stack>
|
|
104
|
-
</
|
|
114
|
+
</Stack>
|
|
115
|
+
</Card>
|
|
116
|
+
|
|
117
|
+
{/* Section: App category */}
|
|
118
|
+
<Card title="App category" description="Choose the category that best describes this app.">
|
|
119
|
+
<Select value={value.appCategory} options={categoryOptions} onValueChange={(c) => onChange({ ...value, appCategory: c })} testID={testID ? `${testID}-category-select` : undefined}/>
|
|
120
|
+
</Card>
|
|
105
121
|
|
|
106
122
|
{/* Section: Primary Color */}
|
|
107
123
|
<Card title="Primary color" description="Set the seed color for your theme palette.">
|
|
@@ -128,27 +144,6 @@ function ThemeComposerInner({ themeId: _themeId, value, onChange, mode, onModeCh
|
|
|
128
144
|
<Select value={value.harmony} options={HARMONY_OPTIONS} onValueChange={(h) => onChange({ ...value, harmony: h })} testID={testID ? `${testID}-harmony-select` : undefined}/>
|
|
129
145
|
</Card>
|
|
130
146
|
|
|
131
|
-
{/* Section: Color tone */}
|
|
132
|
-
<Card title="Color tone" description="Controls the vibrancy and saturation style of the palette.">
|
|
133
|
-
<Stack gap="s">
|
|
134
|
-
<Select value={value.colorTone} options={TONE_OPTIONS} onValueChange={(t) => onChange({ ...value, colorTone: t })} testID={testID ? `${testID}-tone-select` : undefined}/>
|
|
135
|
-
<Stack direction="row" gap="s" align="center">
|
|
136
|
-
<Text tone="muted" variant="caption">
|
|
137
|
-
Background:
|
|
138
|
-
</Text>
|
|
139
|
-
<Badge tone="neutral" emphasis="soft">
|
|
140
|
-
{COLOR_TONE_BACKGROUND_TONE[value.colorTone]}
|
|
141
|
-
</Badge>
|
|
142
|
-
<Text tone="muted" variant="caption">
|
|
143
|
-
Foreground:
|
|
144
|
-
</Text>
|
|
145
|
-
<Badge tone="neutral" emphasis="soft">
|
|
146
|
-
{COLOR_TONE_FOREGROUND_TONE[value.colorTone]}
|
|
147
|
-
</Badge>
|
|
148
|
-
</Stack>
|
|
149
|
-
</Stack>
|
|
150
|
-
</Card>
|
|
151
|
-
|
|
152
147
|
{/* Section: Mode */}
|
|
153
148
|
<Card title="Mode" description="Switch between light and dark presentation.">
|
|
154
149
|
<Tabs value={activeMode} items={MODE_TABS} onValueChange={(m) => onModeChange?.(m)} variant="segmented" testID={testID ? `${testID}-mode-tabs` : undefined}/>
|
|
@@ -157,6 +152,26 @@ function ThemeComposerInner({ themeId: _themeId, value, onChange, mode, onModeCh
|
|
|
157
152
|
{/* Section: Preview */}
|
|
158
153
|
<Card title="Preview" description="A quick look at how your theme renders common controls.">
|
|
159
154
|
<Stack gap="m">
|
|
155
|
+
<Stack gap="xs">
|
|
156
|
+
<Text variant="label">Name</Text>
|
|
157
|
+
<Text>{value.name}</Text>
|
|
158
|
+
</Stack>
|
|
159
|
+
<Stack gap="xs">
|
|
160
|
+
<Text variant="label">Category</Text>
|
|
161
|
+
<Text>{formatAppCategoryLabel(value.appCategory)}</Text>
|
|
162
|
+
</Stack>
|
|
163
|
+
<Stack gap="xs">
|
|
164
|
+
<Text variant="label">Primary color</Text>
|
|
165
|
+
<Text tone="muted" variant="bodySmall">
|
|
166
|
+
{value.primaryColor}
|
|
167
|
+
</Text>
|
|
168
|
+
</Stack>
|
|
169
|
+
<Stack gap="xs">
|
|
170
|
+
<Text variant="label">Harmony</Text>
|
|
171
|
+
<Text tone="muted" variant="bodySmall">
|
|
172
|
+
{value.harmony}
|
|
173
|
+
</Text>
|
|
174
|
+
</Stack>
|
|
160
175
|
<Heading level={4}>Heading</Heading>
|
|
161
176
|
<Text>Body text — this shows default text color and weight.</Text>
|
|
162
177
|
<Text tone="muted" variant="bodySmall">
|
|
@@ -190,7 +205,7 @@ function ThemeComposerInner({ themeId: _themeId, value, onChange, mode, onModeCh
|
|
|
190
205
|
</Card>
|
|
191
206
|
|
|
192
207
|
{/* Submit */}
|
|
193
|
-
{onSubmit ? (<Button tone="primary" emphasis="solid" onPress={
|
|
208
|
+
{onSubmit ? (<Button tone="primary" emphasis="solid" onPress={handleSubmit} testID={testID ? `${testID}-submit` : undefined}>
|
|
194
209
|
Apply theme
|
|
195
210
|
</Button>) : null}
|
|
196
211
|
</Stack>);
|