@castui/cast-ui 0.1.0 → 0.3.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/README.md +201 -5
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/Button.js +2 -4
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Card/Card.d.ts +10 -8
- package/dist/components/Card/Card.d.ts.map +1 -1
- package/dist/components/Card/Card.js +34 -29
- package/dist/components/Card/Card.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/theme/fonts.d.ts +34 -0
- package/dist/theme/fonts.d.ts.map +1 -1
- package/dist/theme/fonts.js +57 -0
- package/dist/theme/fonts.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 +1 -1
- package/dist/theme/index.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -33,6 +33,12 @@ Example:
|
|
|
33
33
|
|
|
34
34
|
See [`design-tokens/DESIGN-TOKENS-SUMMARY.md`](design-tokens/DESIGN-TOKENS-SUMMARY.md) for the full token reference.
|
|
35
35
|
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
For live examples showing Cast UI components and theming in action, see the companion repo:
|
|
39
|
+
|
|
40
|
+
**[Connagh/cast-ui-examples](https://github.com/Connagh/cast-ui-examples)** (in progress)
|
|
41
|
+
|
|
36
42
|
## Project Structure
|
|
37
43
|
|
|
38
44
|
```
|
|
@@ -42,6 +48,9 @@ cast-ui/
|
|
|
42
48
|
Button/
|
|
43
49
|
Button.tsx RN Pressable + Text, theme-aware
|
|
44
50
|
Button.stories.tsx Storybook stories
|
|
51
|
+
Card/
|
|
52
|
+
Card.tsx RN View + Text, theme-aware
|
|
53
|
+
Card.stories.tsx Storybook stories
|
|
45
54
|
theme/
|
|
46
55
|
types.ts CastTheme TypeScript interface
|
|
47
56
|
ThemeProvider.tsx React Context provider + useTheme hook
|
|
@@ -68,6 +77,8 @@ cast-ui/
|
|
|
68
77
|
preview-head.html Google Fonts <link> tags
|
|
69
78
|
.github/workflows/
|
|
70
79
|
chromatic.yml Visual regression testing on push
|
|
80
|
+
adoption.yml Zeroheight adoption tracking on push to main
|
|
81
|
+
publish.yml Publish to npm on push to main
|
|
71
82
|
dist/ Build output (gitignored)
|
|
72
83
|
tsconfig.json Development TypeScript config
|
|
73
84
|
tsconfig.build.json Library build config (excludes stories)
|
|
@@ -113,6 +124,7 @@ Opens at `http://localhost:6006`. Use the paintbrush icon in the toolbar to swit
|
|
|
113
124
|
| `npm run build-storybook` | Build static Storybook (auto-runs `build:tokens`) |
|
|
114
125
|
| `npm run build` | Full library build: tokens + TypeScript compilation to `dist/` |
|
|
115
126
|
| `npm publish` | Publish to npm (auto-runs `build` via `prepublishOnly`) |
|
|
127
|
+
| `npm run zh:track-package` | Register/update package info with Zeroheight (runs automatically via CI) |
|
|
116
128
|
|
|
117
129
|
## Themes
|
|
118
130
|
|
|
@@ -184,6 +196,7 @@ theme.semantic.paragraphSpacing.* body, editorial
|
|
|
184
196
|
theme.semantic.paragraphIndent.* editorial
|
|
185
197
|
theme.semantic.borderRadius.* small, medium, large
|
|
186
198
|
theme.component.button.* All button tokens (padding, colours, variants, states)
|
|
199
|
+
theme.component.card.* All card tokens (padding, colours, typography, elevation)
|
|
187
200
|
```
|
|
188
201
|
|
|
189
202
|
## Components
|
|
@@ -207,6 +220,33 @@ import { Button } from '@castui/cast-ui';
|
|
|
207
220
|
| `disabled` | `boolean` | `false` | Disabled state |
|
|
208
221
|
| `backgroundColor` | `string` | - | Override background colour |
|
|
209
222
|
|
|
223
|
+
### Card
|
|
224
|
+
|
|
225
|
+
React Native `View` + `Text`. Consumes tokens from `theme.component.card` and `theme.semantic`.
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { Card, Button } from '@castui/cast-ui';
|
|
229
|
+
|
|
230
|
+
<Card
|
|
231
|
+
title="Title"
|
|
232
|
+
subtitle="Subtitle"
|
|
233
|
+
body="Body"
|
|
234
|
+
actions={
|
|
235
|
+
<>
|
|
236
|
+
<Button label="Action 1" variant="filled" />
|
|
237
|
+
<Button label="Action 2" variant="outline" />
|
|
238
|
+
</>
|
|
239
|
+
}
|
|
240
|
+
/>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
| Prop | Type | Default | Description |
|
|
244
|
+
|------|------|---------|-------------|
|
|
245
|
+
| `title` | `string` | required | Card heading text |
|
|
246
|
+
| `subtitle` | `string` | - | Optional subtitle below the title |
|
|
247
|
+
| `body` | `string` | - | Optional body text |
|
|
248
|
+
| `actions` | `React.ReactNode` | - | Optional actions row (e.g. Button components) |
|
|
249
|
+
|
|
210
250
|
## Font Handling
|
|
211
251
|
|
|
212
252
|
Components reference font families from the theme tokens. Font **loading** is the consumer's responsibility.
|
|
@@ -224,14 +264,41 @@ const url = googleFontsUrl('luxury');
|
|
|
224
264
|
|
|
225
265
|
### React Native / Expo
|
|
226
266
|
|
|
227
|
-
Load fonts with `expo-font` before rendering
|
|
267
|
+
Load fonts with `expo-font` before rendering. On **Android**, each weight must
|
|
268
|
+
be registered under a distinct name (the Expo Google Fonts convention) because
|
|
269
|
+
Android cannot combine a generic `fontFamily` with a numeric `fontWeight` for
|
|
270
|
+
custom fonts.
|
|
228
271
|
|
|
229
272
|
```ts
|
|
230
273
|
import { useFonts } from 'expo-font';
|
|
231
|
-
import {
|
|
232
|
-
|
|
274
|
+
import {
|
|
275
|
+
Poppins_400Regular,
|
|
276
|
+
Poppins_500Medium,
|
|
277
|
+
Poppins_700Bold,
|
|
278
|
+
} from '@expo-google-fonts/poppins';
|
|
279
|
+
|
|
280
|
+
const [loaded] = useFonts({
|
|
281
|
+
Poppins: Poppins_400Regular, // weight 400 → bare name
|
|
282
|
+
Poppins_500Medium: Poppins_500Medium, // weight 500
|
|
283
|
+
Poppins_700Bold: Poppins_700Bold, // weight 700
|
|
284
|
+
});
|
|
233
285
|
```
|
|
234
286
|
|
|
287
|
+
The naming convention is:
|
|
288
|
+
|
|
289
|
+
| Weight | Registration Key |
|
|
290
|
+
|--------|-----------------|
|
|
291
|
+
| 400 | `"FontName"` |
|
|
292
|
+
| 500 | `"FontName_500Medium"` |
|
|
293
|
+
| 700 | `"FontName_700Bold"` |
|
|
294
|
+
|
|
295
|
+
Components use `resolveFont()` internally so the correct font name is selected
|
|
296
|
+
automatically on each platform:
|
|
297
|
+
|
|
298
|
+
- **iOS / Web** — `{ fontFamily, fontWeight }` passed through unchanged.
|
|
299
|
+
- **Android** — maps to the weight-specific registered name (e.g. `"Poppins_700Bold"`) and sets `fontWeight: 'normal'`.
|
|
300
|
+
- **system-ui** — omits `fontFamily` (platform default) on all platforms.
|
|
301
|
+
|
|
235
302
|
The **White Label** theme uses `system-ui` (platform default) and requires no font loading.
|
|
236
303
|
|
|
237
304
|
| Theme | Fonts to Load |
|
|
@@ -241,6 +308,11 @@ The **White Label** theme uses `system-ui` (platform default) and requires no fo
|
|
|
241
308
|
| Corporate | Inter, Merriweather |
|
|
242
309
|
| Luxury | Playfair Display, Cormorant Garamond |
|
|
243
310
|
|
|
311
|
+
> **Custom Themes:** The convention works for any font — register each weight
|
|
312
|
+
> under `FontName`, `FontName_500Medium`, and `FontName_700Bold` and
|
|
313
|
+
> `resolveFont()` will handle the rest. The `ANDROID_WEIGHT_SUFFIX` map is
|
|
314
|
+
> exported if you need to build registration keys programmatically.
|
|
315
|
+
|
|
244
316
|
## Consumer Installation
|
|
245
317
|
|
|
246
318
|
```bash
|
|
@@ -258,6 +330,8 @@ import {
|
|
|
258
330
|
consumer,
|
|
259
331
|
corporate,
|
|
260
332
|
luxury,
|
|
333
|
+
resolveFont,
|
|
334
|
+
ANDROID_WEIGHT_SUFFIX,
|
|
261
335
|
} from '@castui/cast-ui';
|
|
262
336
|
```
|
|
263
337
|
|
|
@@ -297,15 +371,111 @@ The build script (`src/tokens/build.ts`) reads Figma-exported JSON from `design-
|
|
|
297
371
|
5. Export the component and its types from `src/index.ts`
|
|
298
372
|
6. If the component needs new design tokens, add them to all four `*.tokens.json` files and update `src/theme/types.ts`
|
|
299
373
|
|
|
374
|
+
## Adoption Tracking
|
|
375
|
+
|
|
376
|
+
Design system adoption is measured via [Zeroheight](https://zeroheight.com/). There are two sides to the setup: **this design system repo** and the **consumer app repos** that install it.
|
|
377
|
+
|
|
378
|
+
### This repo (design system)
|
|
379
|
+
|
|
380
|
+
The only command relevant here is `track-package`. It registers `@castui/cast-ui` and its current version with Zeroheight so the dashboard knows what the latest release is.
|
|
381
|
+
|
|
382
|
+
This runs automatically on every push to `main` via the GitHub Actions workflow at `.github/workflows/adoption.yml`. You can also run it locally:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
npm run zh:track-package
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Consumer app repos
|
|
389
|
+
|
|
390
|
+
The adoption CLI is designed to be run **in the repositories that consume this design system**. These are the commands consumers (or their CI pipelines) should run:
|
|
391
|
+
|
|
392
|
+
| Command | What it measures |
|
|
393
|
+
|---------|-----------------|
|
|
394
|
+
| `npx @zeroheight/adoption-cli analyze --component-usage` | Which Cast UI components are imported and how their props are used |
|
|
395
|
+
| `npx @zeroheight/adoption-cli analyze --color-usage` | Hardcoded color values (`#hex`, `rgb()`, etc.) that should be replaced with theme tokens |
|
|
396
|
+
| `npx @zeroheight/adoption-cli monitor-repo` | Which version of `@castui/cast-ui` the consumer app is on |
|
|
397
|
+
|
|
398
|
+
**How color tracking works:** The `--color-usage` flag scans for *non-token* color values — raw hex, rgb, or hsl strings that a developer hardcoded instead of using `theme.semantic.color.*`. A high count means developers are bypassing the design system; a count trending to zero means strong token adoption. Running this in the design system repo itself correctly returns zero results because components only reference theme tokens, never hardcoded colours.
|
|
399
|
+
|
|
400
|
+
### Consumer CI example
|
|
401
|
+
|
|
402
|
+
Consumer teams can automate adoption reporting by adding a workflow to their repos:
|
|
403
|
+
|
|
404
|
+
```yaml
|
|
405
|
+
# .github/workflows/design-system-adoption.yml
|
|
406
|
+
name: "Design System Adoption"
|
|
407
|
+
|
|
408
|
+
on:
|
|
409
|
+
push:
|
|
410
|
+
branches: [main]
|
|
411
|
+
|
|
412
|
+
jobs:
|
|
413
|
+
adoption:
|
|
414
|
+
runs-on: ubuntu-latest
|
|
415
|
+
steps:
|
|
416
|
+
- uses: actions/checkout@v5
|
|
417
|
+
- uses: actions/setup-node@v6
|
|
418
|
+
with:
|
|
419
|
+
node-version: 20
|
|
420
|
+
- run: npm ci
|
|
421
|
+
- name: Report adoption to Zeroheight
|
|
422
|
+
env:
|
|
423
|
+
ZEROHEIGHT_CLIENT_ID: ${{ secrets.ZEROHEIGHT_CLIENT_ID }}
|
|
424
|
+
ZEROHEIGHT_ACCESS_TOKEN: ${{ secrets.ZEROHEIGHT_ACCESS_TOKEN }}
|
|
425
|
+
run: |
|
|
426
|
+
npx @zeroheight/adoption-cli analyze --component-usage --interactive false -r "${{ github.repository }}"
|
|
427
|
+
npx @zeroheight/adoption-cli analyze --color-usage --interactive false -r "${{ github.repository }}"
|
|
428
|
+
npx @zeroheight/adoption-cli monitor-repo --interactive false
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Required secrets
|
|
432
|
+
|
|
433
|
+
All Zeroheight CLI commands in CI require two repository secrets:
|
|
434
|
+
|
|
435
|
+
| Secret | Description |
|
|
436
|
+
|--------|-------------|
|
|
437
|
+
| `ZEROHEIGHT_CLIENT_ID` | OAuth client ID from your Zeroheight account |
|
|
438
|
+
| `ZEROHEIGHT_ACCESS_TOKEN` | Access token from your Zeroheight account |
|
|
439
|
+
|
|
440
|
+
Generate these at **Zeroheight > Account Settings > Access Tokens**.
|
|
441
|
+
|
|
300
442
|
## npm Publishing
|
|
301
443
|
|
|
302
|
-
The package
|
|
444
|
+
The publish workflow (`.github/workflows/publish.yml`) runs on every push to `main`. It **automatically checks** whether the `version` in `package.json` has changed compared to what's already on npm. If the version is the same, the workflow skips silently — no build, no publish. If the version is new, it builds and publishes.
|
|
445
|
+
|
|
446
|
+
This means merging a PR to `main` does **not** automatically publish to npm. Only PRs that include a version bump in `package.json` will trigger a release.
|
|
447
|
+
|
|
448
|
+
**To publish a new version:**
|
|
449
|
+
|
|
450
|
+
1. Create a feature branch and make your changes
|
|
451
|
+
2. Update the `version` field in `package.json` (e.g. `0.1.0` → `0.1.1`)
|
|
452
|
+
3. Push the branch and open a PR to `main`
|
|
453
|
+
4. Chromatic runs and creates status checks on the PR
|
|
454
|
+
5. Review and accept any visual changes in the Chromatic UI
|
|
455
|
+
6. Once Chromatic checks pass, merge the PR
|
|
456
|
+
7. The publish workflow detects the new version, builds tokens + TypeScript, and publishes to npm
|
|
457
|
+
|
|
458
|
+
**If you don't bump the version:** The PR merges normally, Chromatic still runs, but the publish step is skipped. Your code is on `main` but no new npm version is created. This is useful for documentation changes, CI updates, or batching multiple changes before a release.
|
|
459
|
+
|
|
460
|
+
**If Chromatic changes are rejected:** Fix the code on your feature branch, push again. Chromatic re-runs with the new changes. Repeat until the visuals are right, then accept and merge.
|
|
461
|
+
|
|
462
|
+
**Version numbering guide:**
|
|
463
|
+
|
|
464
|
+
| Change type | Bump | Example |
|
|
465
|
+
|-------------|------|---------|
|
|
466
|
+
| Bug fix or minor tweak | Patch | `0.1.0` → `0.1.1` |
|
|
467
|
+
| New component or feature | Minor | `0.1.0` → `0.2.0` |
|
|
468
|
+
| Breaking API change | Major | `0.1.0` → `1.0.0` |
|
|
469
|
+
|
|
470
|
+
**Package details:**
|
|
303
471
|
|
|
304
472
|
- **Entry point:** `dist/index.js` (CJS) with `dist/index.d.ts` type declarations
|
|
305
473
|
- **Included files:** `dist/`, `README.md`, `LICENSE`
|
|
306
474
|
- **Peer dependencies:** `react` (>=18), `react-native` (>=0.72)
|
|
307
475
|
- **License:** MIT
|
|
308
476
|
|
|
477
|
+
You can still publish manually if needed:
|
|
478
|
+
|
|
309
479
|
```bash
|
|
310
480
|
npm login
|
|
311
481
|
npm publish
|
|
@@ -326,8 +496,34 @@ When adding new top-level files or directories, you must add a `!path` entry to
|
|
|
326
496
|
| Workflow | Trigger | Purpose |
|
|
327
497
|
|----------|---------|---------|
|
|
328
498
|
| Chromatic (`.github/workflows/chromatic.yml`) | Every push | Visual regression testing via Storybook snapshots |
|
|
499
|
+
| Adoption Tracking (`.github/workflows/adoption.yml`) | Push to `main` | Registers package version with Zeroheight |
|
|
500
|
+
| Publish to npm (`.github/workflows/publish.yml`) | Push to `main` | Builds and publishes package to npm |
|
|
501
|
+
|
|
502
|
+
**Required secrets:**
|
|
503
|
+
|
|
504
|
+
| Secret | Used by |
|
|
505
|
+
|--------|---------|
|
|
506
|
+
| `CHROMATIC_PROJECT_TOKEN` | Chromatic workflow |
|
|
507
|
+
| `ZEROHEIGHT_CLIENT_ID` | Adoption Tracking workflow |
|
|
508
|
+
| `ZEROHEIGHT_ACCESS_TOKEN` | Adoption Tracking workflow |
|
|
509
|
+
|
|
510
|
+
The Publish workflow authenticates to npm via **OpenID Connect (OIDC)** using npm's Trusted Publishers feature — no `NPM_TOKEN` secret is needed. The trust relationship is configured at [npmjs.com](https://www.npmjs.com/) under **Package Settings → Trusted Publishers**. The `--provenance` flag attaches a verified build attestation to each published version.
|
|
511
|
+
|
|
512
|
+
### Branch Protection
|
|
513
|
+
|
|
514
|
+
Branch protection on `main` ensures that **no code is merged without passing Chromatic visual checks**. This is what prevents unreviewed visual changes from being published to npm.
|
|
515
|
+
|
|
516
|
+
**To set up (one-time, on GitHub):**
|
|
517
|
+
|
|
518
|
+
1. Go to **Settings → Branches** in your repository
|
|
519
|
+
2. Click **Add branch protection rule**
|
|
520
|
+
3. Set **Branch name pattern** to `main`
|
|
521
|
+
4. Enable **Require a pull request before merging**
|
|
522
|
+
5. Enable **Require status checks to pass before merging**
|
|
523
|
+
6. Search for and add **Run Chromatic** (GitHub Actions) and **UI Tests** (any source) as required checks
|
|
524
|
+
7. Click **Save changes**
|
|
329
525
|
|
|
330
|
-
|
|
526
|
+
Once enabled, you can no longer push directly to `main`. All changes go through feature branches and PRs, with Chromatic as a mandatory gate.
|
|
331
527
|
|
|
332
528
|
## Dependencies
|
|
333
529
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAOtB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC;IAChE,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,OAAkB,EAClB,eAAe,EACf,QAAgB,EAChB,GAAG,cAAc,EAClB,EAAE,WAAW,2CAuEb"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Pressable, Text, } from 'react-native';
|
|
4
|
-
import { useTheme } from '../../theme';
|
|
4
|
+
import { useTheme, resolveFont } from '../../theme';
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
// Component
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
@@ -58,13 +58,11 @@ export function Button({ label, variant = 'filled', backgroundColor, disabled =
|
|
|
58
58
|
}),
|
|
59
59
|
opacity: disabled ? 0.6 : 1,
|
|
60
60
|
};
|
|
61
|
-
const fontFamily = bt.fontFamily === 'system-ui' ? undefined : bt.fontFamily;
|
|
62
61
|
const textStyle = {
|
|
63
62
|
color: resolveContent(),
|
|
64
63
|
fontSize: bt.textSize,
|
|
65
|
-
fontWeight: String(bt.fontWeight),
|
|
66
64
|
lineHeight: bt.textSize * bt.lineHeight,
|
|
67
|
-
...(fontFamily
|
|
65
|
+
...resolveFont(bt.fontFamily, bt.fontWeight),
|
|
68
66
|
};
|
|
69
67
|
return (_jsx(Pressable, { ...pressableProps, disabled: disabled, style: containerStyle, ...{
|
|
70
68
|
onHoverIn: () => setHovered(true),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EACL,SAAS,EACT,IAAI,
|
|
1
|
+
{"version":3,"file":"Button.js","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"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"}
|
|
@@ -2,17 +2,19 @@ import React from 'react';
|
|
|
2
2
|
import { type ViewProps } from 'react-native';
|
|
3
3
|
export interface CardProps extends Omit<ViewProps, 'style'> {
|
|
4
4
|
/** Card title text. */
|
|
5
|
-
|
|
6
|
-
/**
|
|
5
|
+
title: string;
|
|
6
|
+
/** Optional subtitle displayed below the title. */
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
/** Optional body text. */
|
|
7
9
|
body?: string;
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
+
/** Optional actions row (e.g. Button components). */
|
|
11
|
+
actions?: React.ReactNode;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
14
|
+
* Card container for grouping related content.
|
|
13
15
|
*
|
|
14
|
-
* Renders as a React Native `View` – works identically
|
|
15
|
-
* and Web (via React Native Web).
|
|
16
|
+
* Renders as a React Native `View` with `Text` children – works identically
|
|
17
|
+
* on iOS, Android, and Web (via React Native Web).
|
|
16
18
|
*/
|
|
17
|
-
export declare function Card({
|
|
19
|
+
export declare function Card({ title, subtitle, body, actions, ...viewProps }: CardProps): import("react/jsx-runtime").JSX.Element;
|
|
18
20
|
//# sourceMappingURL=Card.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;IACzD,uBAAuB;IACvB,
|
|
1
|
+
{"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;IACzD,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAMD;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,EACnB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,GAAG,SAAS,EACb,EAAE,SAAS,2CA0DX"}
|
|
@@ -1,53 +1,58 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { View, Text, } from 'react-native';
|
|
3
|
-
import { useTheme } from '../../theme';
|
|
3
|
+
import { useTheme, resolveFont } from '../../theme';
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// Component
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Card container for grouping related content.
|
|
9
9
|
*
|
|
10
|
-
* Renders as a React Native `View` – works identically
|
|
11
|
-
* and Web (via React Native Web).
|
|
10
|
+
* Renders as a React Native `View` with `Text` children – works identically
|
|
11
|
+
* on iOS, Android, and Web (via React Native Web).
|
|
12
12
|
*/
|
|
13
|
-
export function Card({
|
|
13
|
+
export function Card({ title, subtitle, body, actions, ...viewProps }) {
|
|
14
14
|
const theme = useTheme();
|
|
15
15
|
const ct = theme.component.card;
|
|
16
|
-
|
|
16
|
+
const sem = theme.semantic;
|
|
17
|
+
// --- build styles ----------------------------------------------------------
|
|
17
18
|
const containerStyle = {
|
|
18
19
|
padding: ct.padding,
|
|
19
20
|
gap: ct.gap,
|
|
20
|
-
backgroundColor: ct.background,
|
|
21
21
|
borderRadius: ct.cornerRadius,
|
|
22
|
+
backgroundColor: ct.background,
|
|
22
23
|
borderWidth: ct.strokeWidth,
|
|
23
24
|
borderColor: ct.stroke,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
shadowOpacity: 0.08 * ct.elevation,
|
|
30
|
-
shadowRadius: ct.elevation * 2,
|
|
31
|
-
// Android elevation
|
|
32
|
-
elevation: ct.elevation,
|
|
33
|
-
}),
|
|
25
|
+
elevation: ct.elevation,
|
|
26
|
+
// Web shadow for elevation (React Native Web doesn't map elevation to CSS)
|
|
27
|
+
...(ct.elevation > 0
|
|
28
|
+
? { boxShadow: '0 1px 3px rgba(0,0,0,0.12)' }
|
|
29
|
+
: {}),
|
|
34
30
|
};
|
|
35
|
-
|
|
36
|
-
const headingFontFamily = ct.headingFontFamily === 'system-ui' ? undefined : ct.headingFontFamily;
|
|
37
|
-
const bodyFontFamily = ct.bodyFontFamily === 'system-ui' ? undefined : ct.bodyFontFamily;
|
|
38
|
-
const headingStyle = {
|
|
31
|
+
const titleStyle = {
|
|
39
32
|
fontSize: ct.headingSize,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
lineHeight: ct.headingSize * sem.lineHeight.body,
|
|
34
|
+
letterSpacing: sem.letterSpacing.heading,
|
|
35
|
+
color: sem.color.onSurface,
|
|
36
|
+
...resolveFont(ct.headingFontFamily, ct.headingWeight),
|
|
37
|
+
};
|
|
38
|
+
const subtitleStyle = {
|
|
39
|
+
fontSize: sem.fontSize.small,
|
|
40
|
+
lineHeight: sem.fontSize.small * sem.lineHeight.body,
|
|
41
|
+
letterSpacing: sem.letterSpacing.body,
|
|
42
|
+
color: sem.color.onSurfaceMuted,
|
|
43
|
+
...resolveFont(sem.fontFamily.interface, sem.fontWeight.body),
|
|
43
44
|
};
|
|
44
45
|
const bodyStyle = {
|
|
45
46
|
fontSize: ct.bodySize,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
...(bodyFontFamily
|
|
47
|
+
lineHeight: ct.bodySize * sem.lineHeight.body,
|
|
48
|
+
letterSpacing: sem.letterSpacing.body,
|
|
49
|
+
color: sem.color.onSurfaceMuted,
|
|
50
|
+
...resolveFont(ct.bodyFontFamily, ct.bodyWeight),
|
|
51
|
+
};
|
|
52
|
+
const actionsStyle = {
|
|
53
|
+
flexDirection: 'row',
|
|
54
|
+
gap: ct.gap,
|
|
50
55
|
};
|
|
51
|
-
return (_jsxs(View, { ...viewProps, style: containerStyle, children: [
|
|
56
|
+
return (_jsxs(View, { ...viewProps, style: containerStyle, accessibilityRole: "summary", children: [_jsx(Text, { style: titleStyle, children: title }), subtitle ? _jsx(Text, { style: subtitleStyle, children: subtitle }) : null, body ? _jsx(Text, { style: bodyStyle, children: body }) : null, actions ? _jsx(View, { style: actionsStyle, children: actions }) : null] }));
|
|
52
57
|
}
|
|
53
58
|
//# sourceMappingURL=Card.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Card.js","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,IAAI,EACJ,IAAI,GAIL,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"Card.js","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { CastThemeProvider, useTheme } from './theme';
|
|
2
2
|
export type { CastThemeProviderProps } from './theme';
|
|
3
|
-
export type { CastTheme, ThemeName, SemanticTokens, ComponentTokens, ButtonTokens, } from './theme';
|
|
4
|
-
export { THEME_FONT_FAMILIES, googleFontsUrl } from './theme';
|
|
3
|
+
export type { CastTheme, ThemeName, SemanticTokens, ComponentTokens, ButtonTokens, CardTokens, } from './theme';
|
|
4
|
+
export { THEME_FONT_FAMILIES, googleFontsUrl, resolveFont, ANDROID_WEIGHT_SUFFIX } from './theme';
|
|
5
5
|
export { whiteLabel, consumer, corporate, luxury, } from './tokens/generated';
|
|
6
6
|
export { Button } from './components/Button/Button';
|
|
7
7
|
export type { ButtonProps, ButtonVariant } from './components/Button/Button';
|
|
8
|
+
export { Card } from './components/Card/Card';
|
|
9
|
+
export type { CardProps } from './components/Card/Card';
|
|
8
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,EACZ,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGlG,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,MAAM,GACP,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Theme system
|
|
5
5
|
export { CastThemeProvider, useTheme } from './theme';
|
|
6
|
-
export { THEME_FONT_FAMILIES, googleFontsUrl } from './theme';
|
|
6
|
+
export { THEME_FONT_FAMILIES, googleFontsUrl, resolveFont, ANDROID_WEIGHT_SUFFIX } from './theme';
|
|
7
7
|
// Theme objects
|
|
8
8
|
export { whiteLabel, consumer, corporate, luxury, } from './tokens/generated';
|
|
9
9
|
// Components
|
|
10
10
|
export { Button } from './components/Button/Button';
|
|
11
|
+
export { Card } from './components/Card/Card';
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAUtD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAElG,gBAAgB;AAChB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,MAAM,GACP,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAGpD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/theme/fonts.d.ts
CHANGED
|
@@ -17,7 +17,23 @@
|
|
|
17
17
|
*
|
|
18
18
|
* **Web / Storybook** – fonts are loaded via `<link>` tags in
|
|
19
19
|
* `.storybook/preview-head.html`. No runtime font loading is needed.
|
|
20
|
+
*
|
|
21
|
+
* ### Android font registration convention
|
|
22
|
+
*
|
|
23
|
+
* Android cannot combine a generic `fontFamily` with a numeric `fontWeight`
|
|
24
|
+
* for custom fonts — it silently falls back to the system font. Instead,
|
|
25
|
+
* each weight must be registered under a distinct name that matches the Expo
|
|
26
|
+
* Google Fonts convention:
|
|
27
|
+
*
|
|
28
|
+
* | Weight | Registration key |
|
|
29
|
+
* |--------|------------------------------|
|
|
30
|
+
* | 400 | `"FontName"` |
|
|
31
|
+
* | 500 | `"FontName_500Medium"` |
|
|
32
|
+
* | 700 | `"FontName_700Bold"` |
|
|
33
|
+
*
|
|
34
|
+
* Use {@link resolveFont} in component styles to transparently handle this.
|
|
20
35
|
*/
|
|
36
|
+
import { type TextStyle } from 'react-native';
|
|
21
37
|
import type { ThemeName } from './types';
|
|
22
38
|
/**
|
|
23
39
|
* Map of Google Fonts family names used by each theme.
|
|
@@ -30,4 +46,22 @@ export declare const THEME_FONT_FAMILIES: Record<ThemeName, string[]>;
|
|
|
30
46
|
* Useful if you need to programmatically inject font links on the web.
|
|
31
47
|
*/
|
|
32
48
|
export declare function googleFontsUrl(themeName: ThemeName): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Suffix appended to a font family name on Android to select a specific
|
|
51
|
+
* weight. Matches the Expo Google Fonts registration convention.
|
|
52
|
+
*
|
|
53
|
+
* Weight 400 maps to the bare family name (empty suffix).
|
|
54
|
+
*/
|
|
55
|
+
export declare const ANDROID_WEIGHT_SUFFIX: Record<number, string>;
|
|
56
|
+
/**
|
|
57
|
+
* Return the correct `fontFamily` / `fontWeight` style props for the
|
|
58
|
+
* current platform.
|
|
59
|
+
*
|
|
60
|
+
* - **iOS / Web** — returns `{ fontFamily, fontWeight }` unchanged.
|
|
61
|
+
* - **Android** — maps to a weight-specific registered font name
|
|
62
|
+
* (e.g. `"Poppins_700Bold"`) and resets `fontWeight` to `'normal'`.
|
|
63
|
+
* - **system-ui** — omits `fontFamily` entirely (platform default)
|
|
64
|
+
* and passes `fontWeight` through on all platforms.
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveFont(fontFamily: string, fontWeight: number): Pick<TextStyle, 'fontFamily' | 'fontWeight'>;
|
|
33
67
|
//# sourceMappingURL=fonts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAK3D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CASlE;AAMD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIxD,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,IAAI,CAAC,SAAS,EAAE,YAAY,GAAG,YAAY,CAAC,CAmB9C"}
|
package/dist/theme/fonts.js
CHANGED
|
@@ -17,7 +17,23 @@
|
|
|
17
17
|
*
|
|
18
18
|
* **Web / Storybook** – fonts are loaded via `<link>` tags in
|
|
19
19
|
* `.storybook/preview-head.html`. No runtime font loading is needed.
|
|
20
|
+
*
|
|
21
|
+
* ### Android font registration convention
|
|
22
|
+
*
|
|
23
|
+
* Android cannot combine a generic `fontFamily` with a numeric `fontWeight`
|
|
24
|
+
* for custom fonts — it silently falls back to the system font. Instead,
|
|
25
|
+
* each weight must be registered under a distinct name that matches the Expo
|
|
26
|
+
* Google Fonts convention:
|
|
27
|
+
*
|
|
28
|
+
* | Weight | Registration key |
|
|
29
|
+
* |--------|------------------------------|
|
|
30
|
+
* | 400 | `"FontName"` |
|
|
31
|
+
* | 500 | `"FontName_500Medium"` |
|
|
32
|
+
* | 700 | `"FontName_700Bold"` |
|
|
33
|
+
*
|
|
34
|
+
* Use {@link resolveFont} in component styles to transparently handle this.
|
|
20
35
|
*/
|
|
36
|
+
import { Platform } from 'react-native';
|
|
21
37
|
/**
|
|
22
38
|
* Map of Google Fonts family names used by each theme.
|
|
23
39
|
* The `system-ui` entry means "use the platform default" and requires
|
|
@@ -42,4 +58,45 @@ export function googleFontsUrl(themeName) {
|
|
|
42
58
|
.join('&');
|
|
43
59
|
return `https://fonts.googleapis.com/css2?${params}&display=swap`;
|
|
44
60
|
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Android font-weight resolution
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/**
|
|
65
|
+
* Suffix appended to a font family name on Android to select a specific
|
|
66
|
+
* weight. Matches the Expo Google Fonts registration convention.
|
|
67
|
+
*
|
|
68
|
+
* Weight 400 maps to the bare family name (empty suffix).
|
|
69
|
+
*/
|
|
70
|
+
export const ANDROID_WEIGHT_SUFFIX = {
|
|
71
|
+
400: '',
|
|
72
|
+
500: '_500Medium',
|
|
73
|
+
700: '_700Bold',
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Return the correct `fontFamily` / `fontWeight` style props for the
|
|
77
|
+
* current platform.
|
|
78
|
+
*
|
|
79
|
+
* - **iOS / Web** — returns `{ fontFamily, fontWeight }` unchanged.
|
|
80
|
+
* - **Android** — maps to a weight-specific registered font name
|
|
81
|
+
* (e.g. `"Poppins_700Bold"`) and resets `fontWeight` to `'normal'`.
|
|
82
|
+
* - **system-ui** — omits `fontFamily` entirely (platform default)
|
|
83
|
+
* and passes `fontWeight` through on all platforms.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveFont(fontFamily, fontWeight) {
|
|
86
|
+
const weight = String(fontWeight);
|
|
87
|
+
// system-ui → platform default; just pass through fontWeight
|
|
88
|
+
if (fontFamily === 'system-ui') {
|
|
89
|
+
return { fontWeight: weight };
|
|
90
|
+
}
|
|
91
|
+
// Android needs a weight-specific registered name
|
|
92
|
+
if (Platform.OS === 'android') {
|
|
93
|
+
const suffix = ANDROID_WEIGHT_SUFFIX[fontWeight] ?? '';
|
|
94
|
+
return {
|
|
95
|
+
fontFamily: `${fontFamily}${suffix}`,
|
|
96
|
+
fontWeight: 'normal',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// iOS / Web – pass through unchanged
|
|
100
|
+
return { fontFamily, fontWeight: weight };
|
|
101
|
+
}
|
|
45
102
|
//# sourceMappingURL=fonts.js.map
|
package/dist/theme/fonts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,QAAQ,EAAkB,MAAM,cAAc,CAAC;AAGxD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAgC;IAC9D,aAAa,EAAE,EAAE,EAA8C,iBAAiB;IAChF,QAAQ,EAAE,CAAC,SAAS,CAAC,EAA0C,iBAAiB;IAChF,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,EAA2B,wBAAwB;IACvF,MAAM,EAAE,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,EAAa,kBAAkB;CAClF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAoB;IACjD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,MAAM,GAAG,QAAQ;SACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC;SAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,qCAAqC,MAAM,eAAe,CAAC;AACpE,CAAC;AAED,8EAA8E;AAC9E,iCAAiC;AACjC,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAA2B;IAC3D,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,UAAU;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,UAAkB;IAElB,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAA4B,CAAC;IAE7D,6DAA6D;IAC7D,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;QAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAChC,CAAC;IAED,kDAAkD;IAClD,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACvD,OAAO;YACL,UAAU,EAAE,GAAG,UAAU,GAAG,MAAM,EAAE;YACpC,UAAU,EAAE,QAAQ;SACrB,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
|
package/dist/theme/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { CastThemeProvider, useTheme } from './ThemeProvider';
|
|
2
2
|
export type { CastThemeProviderProps } from './ThemeProvider';
|
|
3
|
-
export { THEME_FONT_FAMILIES, googleFontsUrl } from './fonts';
|
|
3
|
+
export { THEME_FONT_FAMILIES, googleFontsUrl, resolveFont, ANDROID_WEIGHT_SUFFIX } from './fonts';
|
|
4
4
|
export type { CastTheme, ThemeName, SemanticTokens, SemanticColors, SemanticFontFamily, SemanticFontSize, SemanticFontWeight, SemanticLineHeight, SemanticLetterSpacing, SemanticParagraphSpacing, SemanticParagraphIndent, SemanticBorderRadius, ComponentTokens, ButtonTokens, ButtonVariantTokens, ButtonOutlineTokens, ButtonStateTokens, CardTokens, } from './types';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClG,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,GACX,MAAM,SAAS,CAAC"}
|
package/dist/theme/index.js
CHANGED
package/dist/theme/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@castui/cast-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A cross-platform design system for React Native (iOS, Android, Web) with multi-theme support.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"build-storybook": "STORYBOOK_DISABLE_TELEMETRY=1 storybook build",
|
|
18
18
|
"build": "npm run build:tokens && tsc --project tsconfig.build.json",
|
|
19
19
|
"prepublishOnly": "npm run build",
|
|
20
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
20
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
21
|
+
"zh:track-package": "zh-adoption track-package"
|
|
21
22
|
},
|
|
22
23
|
"repository": {
|
|
23
24
|
"type": "git",
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"cross-platform",
|
|
33
34
|
"ui-components"
|
|
34
35
|
],
|
|
35
|
-
"author": "Connagh <connagh@
|
|
36
|
+
"author": "Connagh <connagh@users.noreply.github.com>",
|
|
36
37
|
"license": "MIT",
|
|
37
38
|
"type": "commonjs",
|
|
38
39
|
"bugs": {
|