@xferops/design-guide 0.2.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.
@@ -0,0 +1,124 @@
1
+ ---
2
+ id: forms/field-input
3
+ title: Field and Input Setup
4
+ summary: Wire Field, Input, Select, Textarea, Checkbox, Radio, and Switch into labeled form controls.
5
+ category: forms
6
+ slug: field-input
7
+ tags:
8
+ - forms
9
+ - field
10
+ - input
11
+ - accessibility
12
+ - react
13
+ sourcePath: apps/docs/src/App.tsx
14
+ relatedPaths:
15
+ - packages/ui/src/components/Field
16
+ - packages/ui/src/components/Input
17
+ - packages/ui/src/components/Select
18
+ - packages/ui/src/components/Textarea
19
+ - packages/ui/src/components/Checkbox
20
+ - packages/ui/src/components/Radio
21
+ - packages/ui/src/components/Switch
22
+ ---
23
+
24
+ # Field and Input Setup
25
+
26
+ This guide covers the form control pattern used in the docs app for labeled inputs and selection controls.
27
+
28
+ ## Import the Form Primitives
29
+
30
+ ```tsx
31
+ import {
32
+ Checkbox,
33
+ Field,
34
+ Input,
35
+ Radio,
36
+ Select,
37
+ Stack,
38
+ Switch,
39
+ Textarea
40
+ } from "@xferops/ui";
41
+ ```
42
+
43
+ ## Use `Field` for Labels and Support Text
44
+
45
+ Wrap text-entry controls with `Field` so labels, hint text, and error text stay aligned.
46
+
47
+ ```tsx
48
+ <Field
49
+ labelText="Organization name"
50
+ htmlFor="orgName"
51
+ hintText="Shown in invoices and account settings."
52
+ required
53
+ >
54
+ <Input id="orgName" placeholder="XferOps LLC" />
55
+ </Field>
56
+
57
+ <Field
58
+ labelText="Billing email"
59
+ htmlFor="billingEmail"
60
+ errorText="Enter a valid email address."
61
+ >
62
+ <Input id="billingEmail" type="email" defaultValue="finance@" />
63
+ </Field>
64
+ ```
65
+
66
+ ## Keep Control State in React When Needed
67
+
68
+ Simple toggles and radio groups can use local state.
69
+
70
+ ```tsx
71
+ const [selectedContactMethod, setSelectedContactMethod] = React.useState("email");
72
+ const [notificationsEnabled, setNotificationsEnabled] = React.useState(true);
73
+ ```
74
+
75
+ ## Compose Selection Controls in Stacks
76
+
77
+ Use `Stack` to group related controls without introducing custom layout wrappers.
78
+
79
+ ```tsx
80
+ <Stack gap="md">
81
+ <Select
82
+ labelText="Billing cycle"
83
+ options={[
84
+ { label: "Monthly", value: "monthly" },
85
+ { label: "Quarterly", value: "quarterly" }
86
+ ]}
87
+ defaultValue="monthly"
88
+ />
89
+
90
+ <Textarea
91
+ labelText="Internal note"
92
+ placeholder="Add context for the finance team"
93
+ />
94
+
95
+ <Switch
96
+ labelText="Enable notifications"
97
+ checked={notificationsEnabled}
98
+ onChange={(event) => setNotificationsEnabled(event.currentTarget.checked)}
99
+ />
100
+
101
+ <Checkbox labelText="Send a copy to the account owner" defaultChecked />
102
+
103
+ <Stack direction="row" gap="md">
104
+ <Radio
105
+ name="contactMethod"
106
+ labelText="Email"
107
+ checked={selectedContactMethod === "email"}
108
+ onChange={() => setSelectedContactMethod("email")}
109
+ />
110
+ <Radio
111
+ name="contactMethod"
112
+ labelText="Phone"
113
+ checked={selectedContactMethod === "phone"}
114
+ onChange={() => setSelectedContactMethod("phone")}
115
+ />
116
+ </Stack>
117
+ </Stack>
118
+ ```
119
+
120
+ ## Notes
121
+
122
+ - Use `Field` when a control needs label, hint, or validation messaging.
123
+ - Let `Switch`, `Checkbox`, and `Radio` own their accessible label through `labelText`.
124
+ - Keep layout concerns in `Stack` instead of adding form-specific wrappers.
@@ -0,0 +1,83 @@
1
+ ---
2
+ id: foundations/app-setup
3
+ title: App Setup with Themes and Base Styles
4
+ summary: Bootstrap an app with @xferops/tokens theme classes and the @xferops/ui base layer.
5
+ category: foundations
6
+ slug: app-setup
7
+ tags:
8
+ - foundations
9
+ - setup
10
+ - theming
11
+ - tokens
12
+ - ui
13
+ - vanilla-extract
14
+ sourcePath: apps/docs/src/main.tsx
15
+ relatedPaths:
16
+ - packages/tokens/src/index.ts
17
+ - packages/tokens/src/themes/baseTheme.css.ts
18
+ - packages/tokens/src/themes/partnerTheme.css.ts
19
+ - packages/tokens/src/themes/darkTheme.css.ts
20
+ - packages/tokens/src/themes/ventureTheme.css.ts
21
+ - packages/ui/src/styles/baseLayer.css.ts
22
+ - packages/ui/src/styles/reset.css.ts
23
+ ---
24
+
25
+ # App Setup with Themes and Base Styles
26
+
27
+ Use `@xferops/tokens` to provide a theme class and `@xferops/ui` to apply the shared base layer. This is the minimum setup expected before rendering UI primitives.
28
+
29
+ ## Import Theme Classes and the Base Layer
30
+
31
+ ```tsx
32
+ import React from "react";
33
+ import ReactDOM from "react-dom/client";
34
+ import {
35
+ baseThemeClass,
36
+ darkThemeClass,
37
+ partnerThemeClass,
38
+ ventureThemeClass
39
+ } from "@xferops/tokens";
40
+ import { uiBaseLayerClass } from "@xferops/ui";
41
+ import { App } from "./App";
42
+ ```
43
+
44
+ ## Wrap the App in Both Classes
45
+
46
+ `uiBaseLayerClass` handles the shared reset and base typography. The theme class provides the semantic values consumed by UI components.
47
+
48
+ ```tsx
49
+ function Root() {
50
+ const [theme, setTheme] = React.useState("base");
51
+
52
+ const themeClass =
53
+ theme === "base"
54
+ ? baseThemeClass
55
+ : theme === "partner"
56
+ ? partnerThemeClass
57
+ : theme === "dark"
58
+ ? darkThemeClass
59
+ : ventureThemeClass;
60
+
61
+ return (
62
+ <div className={`${themeClass} ${uiBaseLayerClass}`}>
63
+ <App activeTheme={theme} onThemeChange={setTheme} />
64
+ </div>
65
+ );
66
+ }
67
+ ```
68
+
69
+ ## Render Once at the App Boundary
70
+
71
+ ```tsx
72
+ ReactDOM.createRoot(document.getElementById("root")!).render(
73
+ <React.StrictMode>
74
+ <Root />
75
+ </React.StrictMode>
76
+ );
77
+ ```
78
+
79
+ ## Notes
80
+
81
+ - Always apply a theme class before rendering `@xferops/ui` components, otherwise semantic token lookups from `vars` will be unset.
82
+ - Apply `uiBaseLayerClass` once near the root instead of per component subtree.
83
+ - Keep theme switching outside shared UI components. Brand or tenant changes should happen by swapping token theme classes.
@@ -0,0 +1,125 @@
1
+ ---
2
+ id: foundations/token-theming
3
+ title: Token Consumption and Theme Authoring
4
+ summary: Use semantic token vars in styles and implement brand differences through theme classes.
5
+ category: foundations
6
+ slug: token-theming
7
+ tags:
8
+ - foundations
9
+ - tokens
10
+ - theming
11
+ - vanilla-extract
12
+ - semantic-tokens
13
+ sourcePath: packages/tokens/src/themes/baseTheme.css.ts
14
+ relatedPaths:
15
+ - packages/tokens/src/index.ts
16
+ - packages/tokens/src/themeContract.css.ts
17
+ - packages/tokens/src/themes/baseTheme.css.ts
18
+ - packages/tokens/src/themes/partnerTheme.css.ts
19
+ - packages/tokens/src/themes/darkTheme.css.ts
20
+ - packages/tokens/src/themes/ventureTheme.css.ts
21
+ - packages/tokens/src/primitives/colors.ts
22
+ - packages/tokens/src/primitives/spacing.ts
23
+ - packages/tokens/src/primitives/radii.ts
24
+ - packages/tokens/src/primitives/typography.ts
25
+ - packages/tokens/src/primitives/shadows.ts
26
+ ---
27
+
28
+ # Token Consumption and Theme Authoring
29
+
30
+ `@xferops/tokens` exposes two layers:
31
+
32
+ - primitive scales such as `colors`, `spacing`, `radii`, `typography`, and `shadows`
33
+ - semantic theme variables through `vars`
34
+
35
+ Use primitives when authoring themes. Use `vars` when authoring component styles.
36
+
37
+ ## Import the Public Token API
38
+
39
+ ```ts
40
+ import {
41
+ baseThemeClass,
42
+ colors,
43
+ radii,
44
+ shadows,
45
+ spacing,
46
+ typography,
47
+ vars
48
+ } from "@xferops/tokens";
49
+ ```
50
+
51
+ ## Use `vars` Inside Component Styles
52
+
53
+ Reference semantic values from the theme contract instead of hardcoded colors or spacing.
54
+
55
+ ```ts
56
+ import { style } from "@vanilla-extract/css";
57
+ import { vars } from "@xferops/tokens";
58
+
59
+ export const section = style({
60
+ background: vars.color.bg.surface,
61
+ color: vars.color.text.default,
62
+ border: `1px solid ${vars.color.border.subtle}`,
63
+ borderRadius: vars.radius.lg,
64
+ padding: vars.space.lg,
65
+ boxShadow: vars.shadow.sm
66
+ });
67
+ ```
68
+
69
+ ## Use Primitive Tokens When Defining a Theme
70
+
71
+ Theme files map primitives into the semantic contract.
72
+
73
+ ```ts
74
+ import { createTheme } from "@vanilla-extract/css";
75
+ import { colors, radii, shadows, spacing, typography, vars } from "@xferops/tokens";
76
+
77
+ export const customThemeClass = createTheme(vars, {
78
+ color: {
79
+ bg: {
80
+ page: colors.neutral[50],
81
+ surface: colors.neutral[0],
82
+ surfaceElevated: colors.neutral[0]
83
+ },
84
+ text: {
85
+ default: colors.neutral[900],
86
+ muted: colors.neutral[500],
87
+ inverse: colors.neutral[0],
88
+ danger: colors.red[600]
89
+ },
90
+ border: {
91
+ subtle: colors.neutral[200],
92
+ strong: colors.neutral[300],
93
+ focus: colors.blue[500]
94
+ },
95
+ action: {
96
+ primary: {
97
+ bg: colors.blue[600],
98
+ bgHover: colors.blue[700],
99
+ text: colors.neutral[0]
100
+ },
101
+ secondary: {
102
+ bg: colors.neutral[0],
103
+ bgHover: colors.neutral[100],
104
+ text: colors.neutral[900],
105
+ border: colors.neutral[300]
106
+ }
107
+ }
108
+ },
109
+ space: spacing,
110
+ radius: radii,
111
+ shadow: shadows,
112
+ font: {
113
+ family: typography.fontFamily,
114
+ size: typography.fontSize,
115
+ weight: typography.fontWeight,
116
+ lineHeight: typography.lineHeight
117
+ }
118
+ });
119
+ ```
120
+
121
+ ## Notes
122
+
123
+ - Keep brand-specific decisions in `packages/tokens/src/themes/*`, not in `@xferops/ui` components.
124
+ - Prefer `vars.color.*`, `vars.space.*`, and `vars.font.*` in component styles so all themes stay compatible.
125
+ - Reuse the existing semantic contract unless a real cross-brand need requires adding a new token.
@@ -0,0 +1,124 @@
1
+ ---
2
+ id: navigation/shell
3
+ title: Navigation Shell Setup
4
+ summary: Assemble HeaderBar, HeaderNav, Breadcrumbs, Tabs, and Pagination into a cohesive app shell.
5
+ category: navigation
6
+ slug: shell
7
+ tags:
8
+ - navigation
9
+ - header
10
+ - breadcrumbs
11
+ - tabs
12
+ - pagination
13
+ - layout
14
+ sourcePath: apps/docs/src/App.tsx
15
+ relatedPaths:
16
+ - packages/ui/src/components/Header
17
+ - packages/ui/src/components/Breadcrumbs
18
+ - packages/ui/src/components/Tabs
19
+ - packages/ui/src/components/Pagination
20
+ ---
21
+
22
+ # Navigation Shell Setup
23
+
24
+ This guide shows the navigation composition used in the docs app preview.
25
+
26
+ ## Import the Navigation Primitives
27
+
28
+ ```tsx
29
+ import {
30
+ Avatar,
31
+ Badge,
32
+ Breadcrumbs,
33
+ Button,
34
+ HeaderActions,
35
+ HeaderBar,
36
+ HeaderBrand,
37
+ HeaderNav,
38
+ HeaderNavItem,
39
+ HeaderNavList,
40
+ Pagination,
41
+ Stack,
42
+ Tabs,
43
+ Text
44
+ } from "@xferops/ui";
45
+ ```
46
+
47
+ ## 1. Build the Header
48
+
49
+ Use `HeaderBar` as the top-level container, with brand content on the left, navigation in the center, and actions on the right.
50
+
51
+ ```tsx
52
+ <HeaderBar>
53
+ <HeaderBrand>
54
+ <Badge tone="accent">XO</Badge>
55
+ <Stack gap="none">
56
+ <Text as="span" size="sm" weight="semibold">
57
+ XferOps
58
+ </Text>
59
+ <Text as="span" size="sm" tone="muted">
60
+ Tenant console
61
+ </Text>
62
+ </Stack>
63
+ </HeaderBrand>
64
+
65
+ <HeaderNav aria-label="Primary">
66
+ <HeaderNavList>
67
+ <li>
68
+ <HeaderNavItem href="#" current>
69
+ Overview
70
+ </HeaderNavItem>
71
+ </li>
72
+ <li>
73
+ <HeaderNavItem href="#">Invoices</HeaderNavItem>
74
+ </li>
75
+ <li>
76
+ <HeaderNavItem href="#">Workflows</HeaderNavItem>
77
+ </li>
78
+ </HeaderNavList>
79
+ </HeaderNav>
80
+
81
+ <HeaderActions>
82
+ <Button size="sm" variant="secondary">
83
+ Contact sales
84
+ </Button>
85
+ <Avatar name="Ada Lovelace" />
86
+ </HeaderActions>
87
+ </HeaderBar>
88
+ ```
89
+
90
+ ## 2. Add Secondary Wayfinding
91
+
92
+ Use `Breadcrumbs` for location context and `Tabs` for section switching inside a page.
93
+
94
+ ```tsx
95
+ <Breadcrumbs
96
+ items={[
97
+ { label: "Dashboard", href: "#" },
98
+ { label: "Billing", href: "#" },
99
+ { label: "Invoices" }
100
+ ]}
101
+ />
102
+
103
+ <Tabs
104
+ value={activeTab}
105
+ onValueChange={setActiveTab}
106
+ items={[
107
+ { value: "overview", label: "Overview", content: <Text>Overview content</Text> },
108
+ { value: "activity", label: "Activity", content: <Text>Activity content</Text> },
109
+ { value: "settings", label: "Settings", content: <Text>Settings content</Text> }
110
+ ]}
111
+ />
112
+ ```
113
+
114
+ ## 3. Add Pagination for Datasets
115
+
116
+ ```tsx
117
+ <Pagination totalPages={5} page={activePage} onPageChange={setActivePage} />
118
+ ```
119
+
120
+ ## Notes
121
+
122
+ - `HeaderNavItem` carries the current-page state with `current`.
123
+ - `Tabs` is controlled through `value` and `onValueChange`.
124
+ - `Pagination` is also controlled, so keep the page in React state.
@@ -0,0 +1,134 @@
1
+ ---
2
+ id: overlays/actions
3
+ title: Overlay Actions Setup
4
+ summary: Use Dialog, Drawer, Tooltip, and Popover for focused actions and contextual UI.
5
+ category: overlays
6
+ slug: actions
7
+ tags:
8
+ - overlays
9
+ - dialog
10
+ - drawer
11
+ - tooltip
12
+ - popover
13
+ - interactions
14
+ sourcePath: apps/docs/src/App.tsx
15
+ relatedPaths:
16
+ - packages/ui/src/components/Dialog
17
+ - packages/ui/src/components/Drawer
18
+ - packages/ui/src/components/Tooltip
19
+ - packages/ui/src/components/Popover
20
+ ---
21
+
22
+ # Overlay Actions Setup
23
+
24
+ This guide follows the same overlay pattern used in the docs app.
25
+
26
+ ## Import the Overlay Primitives
27
+
28
+ ```tsx
29
+ import {
30
+ Button,
31
+ Dialog,
32
+ Drawer,
33
+ Field,
34
+ Input,
35
+ Popover,
36
+ Stack,
37
+ Switch,
38
+ Text,
39
+ Tooltip
40
+ } from "@xferops/ui";
41
+ ```
42
+
43
+ ## 1. Keep Open State in React
44
+
45
+ ```tsx
46
+ const [isDialogOpen, setIsDialogOpen] = React.useState(false);
47
+ const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);
48
+ const [notificationsEnabled, setNotificationsEnabled] = React.useState(true);
49
+ ```
50
+
51
+ ## 2. Use Lightweight Triggers for Small Interactions
52
+
53
+ ```tsx
54
+ <Stack direction="row" gap="sm" wrap align="center">
55
+ <Button onClick={() => setIsDialogOpen(true)}>Open dialog</Button>
56
+ <Button variant="secondary" onClick={() => setIsDrawerOpen(true)}>
57
+ Open drawer
58
+ </Button>
59
+ <Tooltip contentText="This action syncs your workspace" side="top">
60
+ <Button variant="secondary">Tooltip trigger</Button>
61
+ </Tooltip>
62
+ <Popover trigger={<Button variant="secondary">Open popover</Button>} side="bottom">
63
+ <Stack gap="xs">
64
+ <Text as="span" weight="semibold">
65
+ Quick actions
66
+ </Text>
67
+ <Button size="sm">Create workspace</Button>
68
+ <Button size="sm" variant="secondary">
69
+ Invite teammate
70
+ </Button>
71
+ </Stack>
72
+ </Popover>
73
+ </Stack>
74
+ ```
75
+
76
+ ## 3. Use `Dialog` for Confirmation
77
+
78
+ ```tsx
79
+ <Dialog
80
+ open={isDialogOpen}
81
+ onOpenChange={setIsDialogOpen}
82
+ titleText="Confirm publish"
83
+ descriptionText="Publishing will make this brand theme available to all tenants."
84
+ footerContent={
85
+ <>
86
+ <Button variant="secondary" onClick={() => setIsDialogOpen(false)}>
87
+ Cancel
88
+ </Button>
89
+ <Button onClick={() => setIsDialogOpen(false)}>Publish</Button>
90
+ </>
91
+ }
92
+ >
93
+ <Text size="sm" tone="muted">
94
+ You can still edit token values later, but tenant apps will pick up this version.
95
+ </Text>
96
+ </Dialog>
97
+ ```
98
+
99
+ ## 4. Use `Drawer` for Side-Panel Forms
100
+
101
+ ```tsx
102
+ <Drawer
103
+ open={isDrawerOpen}
104
+ onOpenChange={setIsDrawerOpen}
105
+ titleText="Tenant settings"
106
+ descriptionText="Adjust workspace-level controls."
107
+ side="right"
108
+ footerContent={
109
+ <>
110
+ <Button variant="secondary" onClick={() => setIsDrawerOpen(false)}>
111
+ Close
112
+ </Button>
113
+ <Button onClick={() => setIsDrawerOpen(false)}>Save changes</Button>
114
+ </>
115
+ }
116
+ >
117
+ <Stack gap="md">
118
+ <Field labelText="Tenant name" htmlFor="drawerTenantName">
119
+ <Input id="drawerTenantName" defaultValue="Acme Corp" />
120
+ </Field>
121
+ <Switch
122
+ labelText="Enable partner override"
123
+ checked={notificationsEnabled}
124
+ onChange={(event) => setNotificationsEnabled(event.currentTarget.checked)}
125
+ />
126
+ </Stack>
127
+ </Drawer>
128
+ ```
129
+
130
+ ## Notes
131
+
132
+ - `Dialog` and `Drawer` are controlled through `open` and `onOpenChange`.
133
+ - `Tooltip` is best for non-critical supporting context.
134
+ - `Popover` is useful for grouped actions that should remain lightweight.
@@ -0,0 +1,77 @@
1
+ ---
2
+ id: primitives/icons-actions
3
+ title: Icons and IconButton
4
+ summary: Use the shared SVG icon primitives for inline affordances and icon-only actions.
5
+ category: primitives
6
+ slug: icons-actions
7
+ tags:
8
+ - icons
9
+ - svg
10
+ - actions
11
+ - button
12
+ - accessibility
13
+ sourcePath: apps/docs/src/App.tsx
14
+ relatedPaths:
15
+ - packages/ui/src/components/Icon
16
+ - packages/ui/src/components/IconButton
17
+ ---
18
+
19
+ # Icons and IconButton
20
+
21
+ This guide covers the starter icon surface exposed from `@xferops/ui`.
22
+
23
+ ## Import the Icon Primitives
24
+
25
+ ```tsx
26
+ import {
27
+ Icon,
28
+ IconButton,
29
+ LogOutIcon,
30
+ MenuIcon,
31
+ SaveIcon,
32
+ SearchIcon
33
+ } from "@xferops/ui";
34
+ ```
35
+
36
+ ## Use Named Icons for Common Actions
37
+
38
+ Named icons keep common affordances consistent across apps while still inheriting text color.
39
+
40
+ ```tsx
41
+ <Stack direction="row" gap="md" align="center">
42
+ <SearchIcon />
43
+ <SaveIcon size="lg" />
44
+ <LogOutIcon aria-label="Sign out" />
45
+ </Stack>
46
+ ```
47
+
48
+ ## Use `Icon` for Custom SVG Paths
49
+
50
+ Use the base `Icon` component when a product-specific glyph is needed but you still want the shared sizing and accessibility behavior.
51
+
52
+ ```tsx
53
+ <Icon aria-label="Workspace exit" size={18}>
54
+ <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
55
+ <path d="M16 17l5-5-5-5" />
56
+ <path d="M21 12H9" />
57
+ </Icon>
58
+ ```
59
+
60
+ ## Pair Icons with `IconButton` for Icon-Only Actions
61
+
62
+ Use the `icon` and `label` props for the common icon-only case. The label is mapped to the button's accessible name.
63
+
64
+ ```tsx
65
+ <Stack direction="row" gap="sm">
66
+ <IconButton icon={<SaveIcon size="sm" />} label="Save item" variant="primary" />
67
+ <IconButton icon={<MenuIcon size="sm" />} label="Open menu" />
68
+ <IconButton icon={<LogOutIcon size="sm" />} label="Sign out" size="sm" />
69
+ </Stack>
70
+ ```
71
+
72
+ ## Notes
73
+
74
+ - Icons default to decorative output unless `title`, `aria-label`, or `aria-labelledby` is supplied.
75
+ - All icons use SVG and inherit color through `currentColor`.
76
+ - `IconButton` also supports the lower-level `children` plus `aria-label` pattern when you need custom content.
77
+ - Prefer named exports from `@xferops/ui` for shared actions, and reserve raw `Icon` usage for genuinely custom shapes.