radix-native 0.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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/dist/index.cjs +4323 -0
  4. package/dist/index.d.cts +861 -0
  5. package/dist/index.d.mts +861 -0
  6. package/dist/index.d.ts +861 -0
  7. package/dist/index.mjs +4285 -0
  8. package/package.json +51 -0
  9. package/src/components/actions/Button.tsx +337 -0
  10. package/src/components/actions/Checkbox.tsx +216 -0
  11. package/src/components/actions/CheckboxCards.tsx +257 -0
  12. package/src/components/actions/CheckboxGroup.tsx +249 -0
  13. package/src/components/actions/index.ts +4 -0
  14. package/src/components/layout/Box.tsx +108 -0
  15. package/src/components/layout/Flex.tsx +149 -0
  16. package/src/components/layout/Grid.tsx +224 -0
  17. package/src/components/layout/index.ts +9 -0
  18. package/src/components/playground/ThemeControls.tsx +456 -0
  19. package/src/components/playground/index.ts +1 -0
  20. package/src/components/typography/Blockquote.tsx +137 -0
  21. package/src/components/typography/Code.tsx +164 -0
  22. package/src/components/typography/Em.tsx +68 -0
  23. package/src/components/typography/Heading.tsx +136 -0
  24. package/src/components/typography/Kbd.tsx +162 -0
  25. package/src/components/typography/Link.tsx +173 -0
  26. package/src/components/typography/Quote.tsx +71 -0
  27. package/src/components/typography/Strong.tsx +70 -0
  28. package/src/components/typography/Text.tsx +140 -0
  29. package/src/components/typography/index.ts +9 -0
  30. package/src/hooks/useResolveColor.ts +24 -0
  31. package/src/hooks/useThemeContext.ts +11 -0
  32. package/src/index.ts +63 -0
  33. package/src/theme/Theme.tsx +12 -0
  34. package/src/theme/ThemeContext.ts +4 -0
  35. package/src/theme/ThemeImpl.tsx +54 -0
  36. package/src/theme/ThemeRoot.tsx +65 -0
  37. package/src/theme/createTheme.tsx +17 -0
  38. package/src/theme/resolveGrayColor.ts +38 -0
  39. package/src/theme/theme.types.ts +95 -0
  40. package/src/tokens/colors/amber.ts +28 -0
  41. package/src/tokens/colors/blue.ts +28 -0
  42. package/src/tokens/colors/bronze.ts +28 -0
  43. package/src/tokens/colors/brown.ts +28 -0
  44. package/src/tokens/colors/crimson.ts +28 -0
  45. package/src/tokens/colors/cyan.ts +28 -0
  46. package/src/tokens/colors/gold.ts +28 -0
  47. package/src/tokens/colors/grass.ts +28 -0
  48. package/src/tokens/colors/gray.ts +28 -0
  49. package/src/tokens/colors/green.ts +28 -0
  50. package/src/tokens/colors/index.ts +36 -0
  51. package/src/tokens/colors/indigo.ts +28 -0
  52. package/src/tokens/colors/iris.ts +28 -0
  53. package/src/tokens/colors/jade.ts +28 -0
  54. package/src/tokens/colors/lime.ts +28 -0
  55. package/src/tokens/colors/mauve.ts +28 -0
  56. package/src/tokens/colors/mint.ts +28 -0
  57. package/src/tokens/colors/olive.ts +28 -0
  58. package/src/tokens/colors/orange.ts +28 -0
  59. package/src/tokens/colors/pink.ts +28 -0
  60. package/src/tokens/colors/plum.ts +28 -0
  61. package/src/tokens/colors/purple.ts +28 -0
  62. package/src/tokens/colors/red.ts +28 -0
  63. package/src/tokens/colors/ruby.ts +28 -0
  64. package/src/tokens/colors/sage.ts +28 -0
  65. package/src/tokens/colors/sand.ts +28 -0
  66. package/src/tokens/colors/sky.ts +28 -0
  67. package/src/tokens/colors/slate.ts +28 -0
  68. package/src/tokens/colors/teal.ts +28 -0
  69. package/src/tokens/colors/tomato.ts +28 -0
  70. package/src/tokens/colors/types.ts +69 -0
  71. package/src/tokens/colors/violet.ts +28 -0
  72. package/src/tokens/colors/yellow.ts +28 -0
  73. package/src/tokens/index.ts +5 -0
  74. package/src/tokens/radius.ts +56 -0
  75. package/src/tokens/scaling.ts +10 -0
  76. package/src/tokens/spacing.ts +21 -0
  77. package/src/tokens/typography.ts +60 -0
  78. package/src/utils/applyScaling.ts +6 -0
  79. package/src/utils/classicEffect.ts +46 -0
  80. package/src/utils/resolveColor.ts +69 -0
  81. package/src/utils/resolveSpace.ts +13 -0
@@ -0,0 +1,56 @@
1
+ export type RadiusToken = 'none' | 'small' | 'medium' | 'large' | 'full'
2
+
3
+ /** Radius level (1–6) — components choose the level that fits their visual size. */
4
+ export type RadiusLevel = 1 | 2 | 3 | 4 | 5 | 6
5
+
6
+ /**
7
+ * Base radius values in pixels at token='medium', scaling=1.
8
+ * Final radius = BASE_RADIUS[level] * RADIUS_FACTOR[token]
9
+ */
10
+ const BASE_RADIUS: Record<RadiusLevel, number> = {
11
+ 1: 3,
12
+ 2: 4,
13
+ 3: 6,
14
+ 4: 8,
15
+ 5: 12,
16
+ 6: 16,
17
+ }
18
+
19
+ /**
20
+ * Multiplier per radius token.
21
+ * none=0, small=0.75, medium=1, large=1.5, full=1.5
22
+ */
23
+ const RADIUS_FACTOR: Record<RadiusToken, number> = {
24
+ none: 0,
25
+ small: 0.75,
26
+ medium: 1,
27
+ large: 1.5,
28
+ full: 1.5,
29
+ }
30
+
31
+ /**
32
+ * Pill radius per token. Only 'full' produces a pill shape (9999); all others are 0.
33
+ */
34
+ const RADIUS_FULL: Record<RadiusToken, number> = {
35
+ none: 0,
36
+ small: 0,
37
+ medium: 0,
38
+ large: 0,
39
+ full: 9999,
40
+ }
41
+
42
+ /**
43
+ * Returns the border radius in pixels for a given token and level.
44
+ * Each component picks the level that matches its visual size.
45
+ */
46
+ export function getRadius(token: RadiusToken, level: RadiusLevel): number {
47
+ return Math.round(BASE_RADIUS[level] * RADIUS_FACTOR[token])
48
+ }
49
+
50
+ /**
51
+ * Returns the full (pill) radius for a given token.
52
+ * Use this for components that should be fully rounded when token='full'.
53
+ */
54
+ export function getFullRadius(token: RadiusToken): number {
55
+ return RADIUS_FULL[token]
56
+ }
@@ -0,0 +1,10 @@
1
+ export type ScalingMode = '90%' | '95%' | '100%' | '105%' | '110%'
2
+
3
+ /** Multiplier applied to space, fontSize and lineHeight */
4
+ export const scalingMap: Record<ScalingMode, number> = {
5
+ '90%': 0.9,
6
+ '95%': 0.95,
7
+ '100%': 1.0,
8
+ '105%': 1.05,
9
+ '110%': 1.1,
10
+ }
@@ -0,0 +1,21 @@
1
+ export type SpaceToken = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
2
+
3
+ /**
4
+ * Margin token — superset of SpaceToken that includes negative values.
5
+ * Matches Radix's marginValues which include '-1' through '-9'.
6
+ */
7
+ export type MarginToken = -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
8
+
9
+ /** Base space scale in pixels (before scaling is applied) */
10
+ export const space: Record<SpaceToken, number> = {
11
+ 0: 0,
12
+ 1: 4,
13
+ 2: 8,
14
+ 3: 12,
15
+ 4: 16,
16
+ 5: 24,
17
+ 6: 32,
18
+ 7: 40,
19
+ 8: 48,
20
+ 9: 64,
21
+ }
@@ -0,0 +1,60 @@
1
+ export type FontSizeToken = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
2
+
3
+ /** Base font size scale in pixels (before scaling is applied) */
4
+ export const fontSize: Record<FontSizeToken, number> = {
5
+ 1: 12,
6
+ 2: 14,
7
+ 3: 16,
8
+ 4: 18,
9
+ 5: 20,
10
+ 6: 24,
11
+ 7: 28,
12
+ 8: 35,
13
+ 9: 60,
14
+ }
15
+
16
+ /** Base line height scale in pixels (before scaling is applied) */
17
+ export const lineHeight: Record<FontSizeToken, number> = {
18
+ 1: 16,
19
+ 2: 20,
20
+ 3: 24,
21
+ 4: 26,
22
+ 5: 28,
23
+ 6: 30,
24
+ 7: 36,
25
+ 8: 40,
26
+ 9: 60,
27
+ }
28
+
29
+ /**
30
+ * Heading line heights in pixels (before scaling).
31
+ * Tighter than body line heights for sizes 2–5.
32
+ */
33
+ export const headingLineHeight: Record<FontSizeToken, number> = {
34
+ 1: 16,
35
+ 2: 18,
36
+ 3: 22,
37
+ 4: 24,
38
+ 5: 26,
39
+ 6: 30,
40
+ 7: 36,
41
+ 8: 40,
42
+ 9: 60,
43
+ }
44
+
45
+ /**
46
+ * Letter spacing as em multipliers per size token.
47
+ * In RN, letterSpacing is in pixels — multiply by the resolved fontSize:
48
+ * letterSpacing = letterSpacingEm[size] * resolvedFontSize
49
+ */
50
+ export const letterSpacingEm: Record<FontSizeToken, number> = {
51
+ 1: 0.0025,
52
+ 2: 0,
53
+ 3: 0,
54
+ 4: -0.0025,
55
+ 5: -0.005,
56
+ 6: -0.00625,
57
+ 7: -0.0075,
58
+ 8: -0.01,
59
+ 9: -0.025,
60
+ }
@@ -0,0 +1,6 @@
1
+ import { scalingMap, type ScalingMode } from '../tokens/scaling'
2
+
3
+ /** Applies the scaling multiplier to a base pixel value and rounds to nearest integer. */
4
+ export function applyScaling(value: number, scaling: ScalingMode): number {
5
+ return Math.round(value * scalingMap[scaling])
6
+ }
@@ -0,0 +1,46 @@
1
+ import { Platform } from 'react-native'
2
+ import type { ViewStyle } from 'react-native'
3
+
4
+ /**
5
+ * Returns ViewStyle that approximates the Radix "classic" 3D effect
6
+ * using only native RN shadow capabilities.
7
+ *
8
+ * Radix CSS uses linear-gradient overlays + inset box-shadows which
9
+ * are not available in RN. We approximate with an outer drop shadow
10
+ * and pressed state removal (simulates "pushed in").
11
+ */
12
+ export function getClassicEffect(
13
+ appearance: 'light' | 'dark',
14
+ options?: { pressed?: boolean; disabled?: boolean },
15
+ ): ViewStyle {
16
+ const isLight = appearance === 'light'
17
+ const pressed = options?.pressed ?? false
18
+ const isDisabled = options?.disabled ?? false
19
+
20
+ // Disabled: no shadow at all — keep it flat like the other disabled variants
21
+ if (isDisabled) return {}
22
+
23
+ // Pressed: remove shadow for "pushed in" feel
24
+ if (pressed) {
25
+ return Platform.select({
26
+ ios: {
27
+ shadowColor: 'transparent',
28
+ shadowOffset: { width: 0, height: 0 },
29
+ shadowOpacity: 0,
30
+ shadowRadius: 0,
31
+ },
32
+ default: { elevation: 0 },
33
+ })!
34
+ }
35
+
36
+ // Normal resting state — subtle drop shadow
37
+ return Platform.select({
38
+ ios: {
39
+ shadowColor: '#000',
40
+ shadowOffset: { width: 0, height: 1 },
41
+ shadowOpacity: isLight ? 0.15 : 0.3,
42
+ shadowRadius: 2,
43
+ },
44
+ default: { elevation: 2 },
45
+ })!
46
+ }
@@ -0,0 +1,69 @@
1
+ import {
2
+ tomato, red, ruby, crimson, pink, plum, purple, violet,
3
+ iris, indigo, blue, cyan, teal, jade, green, grass,
4
+ bronze, gold, brown, orange, amber, yellow, lime, mint, sky,
5
+ gray, mauve, slate, sage, olive, sand,
6
+ } from '../tokens/colors'
7
+ import type { AccentColor, GrayColor, ColorScaleWithModes } from '../tokens/colors/types'
8
+ import type { ThemeColor, ColorOverrides } from '../theme/theme.types'
9
+
10
+ const colorMap: Record<string, ColorScaleWithModes> = {
11
+ tomato, red, ruby, crimson, pink, plum, purple, violet,
12
+ iris, indigo, blue, cyan, teal, jade, green, grass,
13
+ bronze, gold, brown, orange, amber, yellow, lime, mint, sky,
14
+ gray, mauve, slate, sage, olive, sand,
15
+ }
16
+
17
+ /**
18
+ * Resolves a ThemeColor token to a hex string.
19
+ *
20
+ * - `'accent-9'` → step 9 of the active accent color
21
+ * - `'accent-a9'` → alpha step a9 of the active accent color
22
+ * - `'accent-contrast'` → contrast color (text over step 9 background)
23
+ * - `'accent-surface'` → translucent tinted surface
24
+ * - `'gray-9'` → step 9 of the resolved gray color
25
+ * - `'blue-9'` → step 9 of the blue scale (exact, not aliased)
26
+ * - `'#ff0000'` → returned as-is (escape hatch)
27
+ */
28
+ export function resolveColor(
29
+ color: ThemeColor,
30
+ appearance: 'light' | 'dark',
31
+ accentColor: AccentColor,
32
+ resolvedGrayColor: GrayColor,
33
+ colorOverrides?: ColorOverrides,
34
+ ): string {
35
+ let colorName: string
36
+ let step: string
37
+
38
+ if (color.startsWith('accent-')) {
39
+ colorName = accentColor
40
+ step = color.slice('accent-'.length)
41
+ } else if (color.startsWith('gray-')) {
42
+ colorName = resolvedGrayColor
43
+ step = color.slice('gray-'.length)
44
+ } else {
45
+ // Named color: 'blue-9', 'tomato-a3', 'indigo-contrast', etc.
46
+ const dashIdx = color.lastIndexOf('-')
47
+ if (dashIdx <= 0) return color as string
48
+ colorName = color.slice(0, dashIdx)
49
+ step = color.slice(dashIdx + 1)
50
+ }
51
+
52
+ // Check overrides first (per-scale, per-mode — like CSS custom properties in Radix web)
53
+ if (colorOverrides) {
54
+ const scaleOverride = colorOverrides[colorName as keyof ColorOverrides]
55
+ if (scaleOverride) {
56
+ const modeOverride = scaleOverride[appearance]
57
+ if (modeOverride) {
58
+ const value = (modeOverride as Record<string, string>)[step]
59
+ if (value !== undefined) return value
60
+ }
61
+ }
62
+ }
63
+
64
+ // Fall back to built-in scale
65
+ const scale = colorMap[colorName]?.[appearance]
66
+ if (!scale) return color as string
67
+
68
+ return (scale as unknown as Record<string, string>)[step] ?? (color as string)
69
+ }
@@ -0,0 +1,13 @@
1
+ import { space, type SpaceToken, type MarginToken } from '../tokens/spacing'
2
+ import { scalingMap, type ScalingMode } from '../tokens/scaling'
3
+
4
+ /**
5
+ * Returns the resolved pixel value for a SpaceToken or MarginToken with scaling applied.
6
+ * Negative margin tokens return a negative pixel value using the absolute space scale.
7
+ */
8
+ export function resolveSpace(token: MarginToken, scaling: ScalingMode): number {
9
+ if (token === 0) return 0
10
+ const sign = token < 0 ? -1 : 1
11
+ const abs = Math.abs(token) as SpaceToken
12
+ return sign * Math.round(space[abs] * scalingMap[scaling])
13
+ }