@joinskips/brand 5.0.0-develop.117 → 5.0.0-develop.119

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 CHANGED
@@ -13,4 +13,11 @@ import tokens from '@joinskips/brand/tokens/skips-design-tokens.json' with { typ
13
13
  @import '@joinskips/brand/fonts/onest/onest.css';
14
14
  ```
15
15
 
16
+ A Tailwind 4 app imports the tokens as its theme. The colours sit under `@variant light` and `@variant dark`, the way
17
+ Uniwind reads them.
18
+
19
+ ```css
20
+ @import '@joinskips/brand/tokens/skips-tailwind-theme.css';
21
+ ```
22
+
16
23
  `manifest.json` lists every file with its hash and size. Onest's licence is `fonts/onest/OFL.txt`.
@@ -62,6 +62,9 @@ export declare function CodeStep({ words, length, noMatch, ranOut, resendAfterMs
62
62
  onResend: () => void;
63
63
  onChangeEmail: () => void;
64
64
  }): import("react").JSX.Element;
65
+ export declare const DOT_REST_OPACITY = 0.35;
66
+ export declare const DOT_RISE = 3;
67
+ export declare const DOT_STILL_OPACITY = 0.6;
65
68
  /** Skip writing a reply: three dots rising in turn in Skip's bubble beside Skip's mark, announced as a status. */
66
69
  export declare function SkipTyping({ label, skipMark }: {
67
70
  label: string;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
- import { ActivityIndicator, Animated, Image, Pressable, Text, TextInput, View } from 'react-native';
3
+ import { AccessibilityInfo, ActivityIndicator, Animated, Image, Pressable, Text, TextInput, View } from 'react-native';
4
4
  import { DOT_CYCLE_MS } from "../skipTyping.js";
5
5
  import { codeDigits, completesCode, EMAIL_MAX_LENGTH, givenEmail, givenName, NAME_MAX_LENGTH, RESEND_AFTER_MS, resendClock, } from "../welcome.js";
6
6
  import { useColor } from "./parts.js";
@@ -74,17 +74,38 @@ export function CodeStep({ words, length, noMatch, ranOut, resendAfterMs = RESEN
74
74
  setCode(digits);
75
75
  if (completesCode(digits, code, length))
76
76
  onSubmit(digits);
77
- }, accessibilityLabel: words.codeLabel, "aria-invalid": error !== null, inputMode: "numeric", autoComplete: "one-time-code", textContentType: "oneTimeCode", caretHidden: true, className: "absolute inset-0 rounded-control text-transparent" })] }), error ? (_jsx(Text, { role: "alert", className: "text-center font-ui text-sm text-error-ink", children: error })) : null, _jsxs(View, { className: "flex-row flex-wrap items-center justify-center gap-2", children: [_jsx(Pill, { label: words.changeEmail, look: "flat", onPress: onChangeEmail }), _jsx(NewCode, { waitMs: resendAfterMs, words: words, onResend: onResend }), _jsx(Pill, { label: words.codeSubmit, onPress: () => {
77
+ }, accessibilityLabel: words.codeLabel, "aria-invalid": error !== null, inputMode: "numeric", autoComplete: "one-time-code", textContentType: "oneTimeCode", caretHidden: true, className: "absolute inset-0 rounded-control text-transparent" })] }), error ? (_jsx(Text, { role: "alert", className: "text-center font-ui text-sm text-error-ink", children: error })) : null, _jsxs(View, { className: "flex-row flex-wrap items-center justify-center gap-2", children: [_jsx(Pill, { label: words.changeEmail, look: "flat", onPress: onChangeEmail }), _jsx(NewCode, { waitMs: resendAfterMs, words: words, onResend: onResend }), _jsx(Pill, { label: words.codeSubmit, disabled: code.length < length, onPress: () => {
78
78
  onSubmit(code);
79
79
  } })] })] }));
80
80
  }
81
81
  const DOTS = [0, 1, 2];
82
82
  const DOT_SIZE = 6;
83
+ // A dot at rest is faint, rises this far at the top of its turn, and holds at the still strength for a visitor who
84
+ // asks for less motion.
85
+ export const DOT_REST_OPACITY = 0.35;
86
+ export const DOT_RISE = 3;
87
+ export const DOT_STILL_OPACITY = 0.6;
88
+ // Null until the device has answered, so the dots never start before a visitor who wants them still is known.
89
+ function useReducedMotion() {
90
+ const [reduced, setReduced] = useState(null);
91
+ useEffect(() => {
92
+ void AccessibilityInfo.isReduceMotionEnabled().then(setReduced);
93
+ const change = AccessibilityInfo.addEventListener('reduceMotionChanged', setReduced);
94
+ return () => {
95
+ change.remove();
96
+ };
97
+ }, []);
98
+ return reduced;
99
+ }
83
100
  /** Skip writing a reply: three dots rising in turn in Skip's bubble beside Skip's mark, announced as a status. */
84
101
  export function SkipTyping({ label, skipMark }) {
85
102
  const dotColor = useColor('ink-secondary');
103
+ const reduced = useReducedMotion();
104
+ const still = reduced === true;
86
105
  const rise = useRef(DOTS.map(() => new Animated.Value(0))).current;
87
106
  useEffect(() => {
107
+ if (reduced !== false)
108
+ return undefined;
88
109
  const step = DOT_CYCLE_MS / (2 * DOTS.length);
89
110
  const loop = Animated.loop(Animated.stagger(step, rise.map((value) => Animated.sequence([
90
111
  Animated.timing(value, { toValue: 1, duration: step, useNativeDriver: true }),
@@ -94,12 +115,17 @@ export function SkipTyping({ label, skipMark }) {
94
115
  return () => {
95
116
  loop.stop();
96
117
  };
97
- }, [rise]);
118
+ }, [rise, reduced]);
98
119
  return (_jsxs(View, { className: "mt-3 flex-row items-end gap-2", children: [_jsx(Image, { source: skipMark, "aria-hidden": true, style: { width: 32, height: 32 }, className: "rounded-control" }), _jsx(View, { role: "status", accessibilityLabel: label, className: "flex-row items-center gap-1 rounded-[20px] rounded-bl-[7px] border border-line-subtle bg-surface px-4 py-4", children: rise.map((value, dot) => (_jsx(Animated.View, { style: {
99
120
  width: DOT_SIZE,
100
121
  height: DOT_SIZE,
101
122
  borderRadius: DOT_SIZE / 2,
102
123
  backgroundColor: dotColor,
103
- transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [0, -DOT_SIZE] }) }],
124
+ ...(still
125
+ ? { opacity: DOT_STILL_OPACITY }
126
+ : {
127
+ opacity: value.interpolate({ inputRange: [0, 1], outputRange: [DOT_REST_OPACITY, 1] }),
128
+ transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [0, -DOT_RISE] }) }],
129
+ }),
104
130
  } }, DOTS[dot]))) })] }));
105
131
  }
package/manifest.json CHANGED
@@ -484,6 +484,10 @@
484
484
  "tokens/skips-design-tokens.json": {
485
485
  "sha256": "bd0deecdedbad1a5ab28d0ec3231095d2b4691390a7d42257d259f3197f181ef",
486
486
  "bytes": 3205
487
+ },
488
+ "tokens/skips-tailwind-theme.css": {
489
+ "sha256": "00fef53e4e6c3bde68e93a2e5f0ba28edcf17e6da5df6823c7601461462364af",
490
+ "bytes": 3240
487
491
  }
488
492
  }
489
493
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joinskips/brand",
3
- "version": "5.0.0-develop.117",
3
+ "version": "5.0.0-develop.119",
4
4
  "description": "Skips design tokens, Onest, logos, favicons, interface icons, member pictures and app icons.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://brand.joinskips.com",
@@ -20,10 +20,10 @@
20
20
  "validator": "^13.15.35"
21
21
  },
22
22
  "peerDependencies": {
23
- "react": "^19.3.0",
23
+ "react": "^19.0.0",
24
24
  "react-native": ">=0.81.0",
25
- "react-native-web": "~0.21.4",
26
- "tailwindcss": "^4.3.3",
25
+ "react-native-web": "~0.21.0",
26
+ "tailwindcss": "^4.0.0",
27
27
  "uniwind": "1.13.2"
28
28
  },
29
29
  "peerDependenciesMeta": {
@@ -0,0 +1,106 @@
1
+ /* Written by scripts/derive.py from skips-design-tokens.json; do not edit by hand. */
2
+ @theme {
3
+ --font-ui: 'Onest Variable', Onest, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
4
+ --text-xs: 12px;
5
+ --text-xs--line-height: 1.5;
6
+ --text-sm: 13px;
7
+ --text-sm--line-height: 1.5;
8
+ --text-base: 15px;
9
+ --text-base--line-height: 1.6;
10
+ --text-md: 16px;
11
+ --text-md--line-height: 1.6;
12
+ --text-lg: 18px;
13
+ --text-lg--line-height: 1.55;
14
+ --text-xl: 22px;
15
+ --text-xl--line-height: 1.4;
16
+ --text-2xl: 28px;
17
+ --text-2xl--line-height: 1.2;
18
+ --text-3xl: 36px;
19
+ --text-3xl--line-height: 1.1;
20
+ --text-4xl: 40px;
21
+ --text-4xl--line-height: 1.05;
22
+ --text-5xl: 54px;
23
+ --text-5xl--line-height: 1.03;
24
+ --text-6xl: 72px;
25
+ --text-6xl--line-height: 1.02;
26
+ --font-weight-light: 300;
27
+ --font-weight-regular: 400;
28
+ --font-weight-medium: 500;
29
+ --font-weight-semibold: 600;
30
+ --font-weight-bold: 700;
31
+ --font-weight-heavy: 800;
32
+ --radius-control: 8px;
33
+ --radius-card: 12px;
34
+ --radius-overlay: 16px;
35
+ --radius-pill: 9999px;
36
+ --shadow-card: 0 1px 2px rgba(33, 38, 41, 0.04);
37
+ --shadow-lift: 0 4px 12px rgba(33, 38, 41, 0.06);
38
+ --shadow-lift-hover: 0 8px 28px rgba(33, 38, 41, 0.14);
39
+ --shadow-menu: 0 8px 24px rgba(33, 38, 41, 0.12);
40
+ --shadow-modal: 0 24px 64px rgba(33, 38, 41, 0.18);
41
+ --ease-standard: cubic-bezier(0.2, 0, 0, 1);
42
+ --ease-pulse: cubic-bezier(0.4, 0, 0.6, 1);
43
+ --container-content: 1200px;
44
+ --container-reading: 720px;
45
+ --spacing-control: 48px;
46
+ --spacing-control-compact: 44px;
47
+ --spacing-header-short: 60px;
48
+ --spacing-header-tall: 76px;
49
+ --spacing-gutter-phone: 16px;
50
+ --spacing-gutter-tablet: 24px;
51
+ --spacing-gutter-desktop: 32px;
52
+ }
53
+
54
+ @layer theme {
55
+ :root {
56
+ @variant light {
57
+ --color-page: #f8f5f2;
58
+ --color-surface: #ffffff;
59
+ --color-muted: #f5f6f7;
60
+ --color-paper: #f7f5f0;
61
+ --color-ink: #212629;
62
+ --color-ink-secondary: #5b5b57;
63
+ --color-ink-placeholder: #64645f;
64
+ --color-line-subtle: #d9d9d6;
65
+ --color-line-control: #85857f;
66
+ --color-accent: #f7ad00;
67
+ --color-accent-ink: #212629;
68
+ --color-action: #242e2b;
69
+ --color-action-hover: #314039;
70
+ --color-action-pressed: #1a231f;
71
+ --color-action-ink: #ffffff;
72
+ --color-error: #fff1f0;
73
+ --color-error-ink: #b42318;
74
+ --color-warning: #fff5d6;
75
+ --color-warning-ink: #854d0e;
76
+ --color-success: #dcf3e3;
77
+ --color-success-ink: #166534;
78
+ --color-focus: #212629;
79
+ }
80
+
81
+ @variant dark {
82
+ --color-page: #0f1110;
83
+ --color-surface: #181a19;
84
+ --color-muted: #1f2221;
85
+ --color-paper: #1b1b18;
86
+ --color-ink: #f5f6f4;
87
+ --color-ink-secondary: #b4b4ae;
88
+ --color-ink-placeholder: #8d8d87;
89
+ --color-line-subtle: #2c2e2d;
90
+ --color-line-control: #5a5c59;
91
+ --color-accent: #f7ad00;
92
+ --color-accent-ink: #212629;
93
+ --color-action: #3a4a44;
94
+ --color-action-hover: #46574f;
95
+ --color-action-pressed: #2f3c37;
96
+ --color-action-ink: #ffffff;
97
+ --color-error: #3a1b19;
98
+ --color-error-ink: #ff8a80;
99
+ --color-warning: #3a2e10;
100
+ --color-warning-ink: #f5c451;
101
+ --color-success: #12301f;
102
+ --color-success-ink: #7ddc9c;
103
+ --color-focus: #f5f6f4;
104
+ }
105
+ }
106
+ }