@danielthurau/atlas-labs-codex 0.1.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.
Files changed (42) hide show
  1. package/README.md +138 -0
  2. package/components/react/Badge/Badge.module.css +38 -0
  3. package/components/react/Badge/Badge.stories.tsx +79 -0
  4. package/components/react/Badge/Badge.tsx +42 -0
  5. package/components/react/Badge/index.ts +2 -0
  6. package/components/react/Button/Button.module.css +118 -0
  7. package/components/react/Button/Button.stories.tsx +108 -0
  8. package/components/react/Button/Button.tsx +73 -0
  9. package/components/react/Button/index.ts +2 -0
  10. package/components/react/Card/Card.module.css +59 -0
  11. package/components/react/Card/Card.stories.tsx +114 -0
  12. package/components/react/Card/Card.tsx +79 -0
  13. package/components/react/Card/index.ts +11 -0
  14. package/components/react/Input/Input.module.css +63 -0
  15. package/components/react/Input/Input.stories.tsx +88 -0
  16. package/components/react/Input/Input.tsx +50 -0
  17. package/components/react/Input/index.ts +2 -0
  18. package/components/react/Modal/Modal.module.css +103 -0
  19. package/components/react/Modal/Modal.stories.tsx +119 -0
  20. package/components/react/Modal/Modal.tsx +75 -0
  21. package/components/react/Modal/index.ts +2 -0
  22. package/components/react/RefreshButton/RefreshButton.module.css +202 -0
  23. package/components/react/RefreshButton/RefreshButton.stories.tsx +43 -0
  24. package/components/react/RefreshButton/RefreshButton.tsx +222 -0
  25. package/components/react/RefreshButton/index.ts +2 -0
  26. package/components/react/Tabs/Tabs.module.css +58 -0
  27. package/components/react/Tabs/Tabs.stories.tsx +101 -0
  28. package/components/react/Tabs/Tabs.tsx +62 -0
  29. package/components/react/Tabs/index.ts +2 -0
  30. package/components/react/Toast/Toast.module.css +145 -0
  31. package/components/react/Toast/Toast.stories.tsx +143 -0
  32. package/components/react/Toast/Toast.tsx +123 -0
  33. package/components/react/Toast/index.ts +2 -0
  34. package/components/react/index.ts +31 -0
  35. package/lib/index.ts +7 -0
  36. package/lib/utils.ts +32 -0
  37. package/package.json +95 -0
  38. package/public/fonts/MartianMono-OFL.txt +93 -0
  39. package/public/fonts/MartianMono-VariableFont_wdth,wght.ttf +0 -0
  40. package/themes/css/base.css +142 -0
  41. package/themes/css/theme-dark.css +48 -0
  42. package/themes/css/theme-light.css +49 -0
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # Personal Design Codex
2
+
3
+ A private repository that acts as a single source of truth for all design-related decisions. This codex centralizes typography rules, color palettes, spacing systems, and UI components in one place.
4
+
5
+ ## Philosophy
6
+
7
+ This is a **personal craftsman repository**—a tidy workshop where design decisions are explicit, documented, and reusable by your future self.
8
+
9
+ ---
10
+
11
+ ## Using in Other Projects
12
+
13
+ Install from npm:
14
+
15
+ ```bash
16
+ npm install @atlas-labs/design-codex
17
+ ```
18
+
19
+ Then import components, styles, and utilities:
20
+
21
+ ```tsx
22
+ // Import components
23
+ import { Button, Card, Input, Badge, RefreshButton } from '@atlas-labs/design-codex';
24
+
25
+ // Import utilities
26
+ import { formatRelativeTime, clsx } from '@atlas-labs/design-codex/lib';
27
+
28
+ // Import CSS (in your layout.tsx or entry point)
29
+ import '@atlas-labs/design-codex/themes/css/base.css';
30
+ import '@atlas-labs/design-codex/themes/css/theme-light.css';
31
+ import '@atlas-labs/design-codex/themes/css/theme-dark.css';
32
+ ```
33
+
34
+ ### Font Setup
35
+
36
+ Copy the Martian Mono font to your project's public folder, or load it in your CSS:
37
+
38
+ ```css
39
+ @font-face {
40
+ font-family: 'Martian Mono';
41
+ src: url('/fonts/MartianMono-VariableFont_wdth,wght.ttf') format('truetype');
42
+ font-weight: 100 800;
43
+ font-display: swap;
44
+ }
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Development
50
+
51
+ ### Prerequisites
52
+
53
+ **Node.js 18+** is required. Check your version:
54
+
55
+ ```bash
56
+ node --version # Should be v18.x or higher
57
+ ```
58
+
59
+ If you need to upgrade, use [nvm](https://github.com/nvm-sh/nvm):
60
+
61
+ ```bash
62
+ nvm install 20
63
+ nvm use 20
64
+ ```
65
+
66
+ ### Quick Start
67
+
68
+ ```bash
69
+ # Install dependencies
70
+ npm install
71
+
72
+ # Run the playground
73
+ npm run dev
74
+
75
+ # Run Storybook
76
+ npm run storybook
77
+ ```
78
+
79
+ ## Structure
80
+
81
+ ```
82
+ ├── codex/ # Design documentation
83
+ │ ├── principles.md # Core design principles
84
+ │ ├── typography.md # Type system
85
+ │ ├── color.md # Color strategy
86
+ │ ├── spacing.md # Spacing scale
87
+ │ ├── components.md # Component guidelines
88
+ │ └── patterns/ # UI pattern recipes
89
+ ├── themes/css/ # CSS custom properties
90
+ │ ├── base.css # Primitives & component tokens
91
+ │ ├── theme-light.css # Light mode
92
+ │ └── theme-dark.css # Dark mode
93
+ ├── components/react/ # React component library
94
+ │ ├── Button/
95
+ │ ├── Input/
96
+ │ ├── Card/
97
+ │ ├── Badge/
98
+ │ ├── Modal/
99
+ │ ├── Toast/
100
+ │ ├── Tabs/
101
+ │ └── RefreshButton/ # AWS-style auto-refresh button
102
+ ├── lib/ # Shared utilities
103
+ │ ├── index.ts # Exports all utilities
104
+ │ └── utils.ts # formatRelativeTime, clsx
105
+ ├── app/ # Next.js playground
106
+ ├── public/fonts/ # Martian Mono font files
107
+ └── .storybook/ # Storybook configuration
108
+ ```
109
+
110
+ ## Design Tokens
111
+
112
+ All visual values are defined as CSS custom properties in `themes/css/`. The token architecture follows three layers:
113
+
114
+ 1. **Primitive tokens** — Raw values (colors, spacing units)
115
+ 2. **Semantic tokens** — Meaningful mappings (`--color-text-primary`)
116
+ 3. **Component tokens** — Component-specific overrides (`--button-radius`)
117
+
118
+ ## Themes
119
+
120
+ The codex supports light and dark themes via CSS custom properties. Theme switching uses:
121
+
122
+ ```html
123
+ <html data-theme="light">
124
+ <!-- or -->
125
+ <html data-theme="dark">
126
+ ```
127
+
128
+ ## Components
129
+
130
+ All components:
131
+ - Use tokens exclusively (no hardcoded values)
132
+ - Are built on Radix UI primitives for accessibility
133
+ - Encode taste, not flexibility
134
+
135
+ ## License
136
+
137
+ Private repository. Personal use only.
138
+
@@ -0,0 +1,38 @@
1
+ .badge {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ padding: var(--badge-padding-y) var(--badge-padding-x);
6
+ font-size: var(--badge-font-size);
7
+ font-weight: var(--font-medium);
8
+ line-height: 1;
9
+ border-radius: var(--badge-radius);
10
+ white-space: nowrap;
11
+ }
12
+
13
+ /* Variants */
14
+ .default {
15
+ background-color: var(--color-bg-subtle);
16
+ color: var(--color-text-secondary);
17
+ }
18
+
19
+ .primary {
20
+ background-color: var(--color-intent-primary-bg);
21
+ color: var(--color-intent-primary);
22
+ }
23
+
24
+ .success {
25
+ background-color: var(--color-intent-success-bg);
26
+ color: var(--color-intent-success);
27
+ }
28
+
29
+ .warning {
30
+ background-color: var(--color-intent-warning-bg);
31
+ color: var(--color-intent-warning);
32
+ }
33
+
34
+ .danger {
35
+ background-color: var(--color-intent-danger-bg);
36
+ color: var(--color-intent-danger);
37
+ }
38
+
@@ -0,0 +1,79 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Badge } from "./Badge";
3
+
4
+ const meta: Meta<typeof Badge> = {
5
+ title: "Components/Badge",
6
+ component: Badge,
7
+ parameters: {
8
+ layout: "centered",
9
+ },
10
+ tags: ["autodocs"],
11
+ argTypes: {
12
+ variant: {
13
+ control: "select",
14
+ options: ["default", "primary", "success", "warning", "danger"],
15
+ },
16
+ },
17
+ };
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof meta>;
21
+
22
+ export const Default: Story = {
23
+ args: {
24
+ children: "Default",
25
+ variant: "default",
26
+ },
27
+ };
28
+
29
+ export const Primary: Story = {
30
+ args: {
31
+ children: "Primary",
32
+ variant: "primary",
33
+ },
34
+ };
35
+
36
+ export const Success: Story = {
37
+ args: {
38
+ children: "Success",
39
+ variant: "success",
40
+ },
41
+ };
42
+
43
+ export const Warning: Story = {
44
+ args: {
45
+ children: "Warning",
46
+ variant: "warning",
47
+ },
48
+ };
49
+
50
+ export const Danger: Story = {
51
+ args: {
52
+ children: "Danger",
53
+ variant: "danger",
54
+ },
55
+ };
56
+
57
+ export const AllVariants: Story = {
58
+ render: () => (
59
+ <div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
60
+ <Badge variant="default">Default</Badge>
61
+ <Badge variant="primary">Primary</Badge>
62
+ <Badge variant="success">Success</Badge>
63
+ <Badge variant="warning">Warning</Badge>
64
+ <Badge variant="danger">Danger</Badge>
65
+ </div>
66
+ ),
67
+ };
68
+
69
+ export const StatusBadges: Story = {
70
+ render: () => (
71
+ <div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
72
+ <Badge variant="success">Active</Badge>
73
+ <Badge variant="warning">Pending</Badge>
74
+ <Badge variant="danger">Failed</Badge>
75
+ <Badge variant="default">Draft</Badge>
76
+ </div>
77
+ ),
78
+ };
79
+
@@ -0,0 +1,42 @@
1
+ import { forwardRef, type HTMLAttributes, type ReactNode } from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { clsx } from "clsx";
4
+ import styles from "./Badge.module.css";
5
+
6
+ const badgeVariants = cva(styles.badge, {
7
+ variants: {
8
+ variant: {
9
+ default: styles.default,
10
+ primary: styles.primary,
11
+ success: styles.success,
12
+ warning: styles.warning,
13
+ danger: styles.danger,
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ });
20
+
21
+ export interface BadgeProps
22
+ extends HTMLAttributes<HTMLSpanElement>,
23
+ VariantProps<typeof badgeVariants> {
24
+ children: ReactNode;
25
+ }
26
+
27
+ export const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
28
+ ({ className, variant, children, ...props }, ref) => {
29
+ return (
30
+ <span
31
+ ref={ref}
32
+ className={clsx(badgeVariants({ variant }), className)}
33
+ {...props}
34
+ >
35
+ {children}
36
+ </span>
37
+ );
38
+ }
39
+ );
40
+
41
+ Badge.displayName = "Badge";
42
+
@@ -0,0 +1,2 @@
1
+ export { Badge, type BadgeProps } from "./Badge";
2
+
@@ -0,0 +1,118 @@
1
+ .button {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ gap: var(--space-2);
6
+ font-weight: var(--button-font-weight);
7
+ border-radius: var(--button-radius);
8
+ transition: all 150ms ease;
9
+ cursor: pointer;
10
+ border: none;
11
+ text-decoration: none;
12
+ white-space: nowrap;
13
+ }
14
+
15
+ .button:focus-visible {
16
+ outline: none;
17
+ box-shadow: var(--focus-ring);
18
+ }
19
+
20
+ .button:disabled {
21
+ opacity: 0.5;
22
+ cursor: not-allowed;
23
+ }
24
+
25
+ /* Variants */
26
+ .primary {
27
+ background-color: var(--color-intent-primary);
28
+ color: var(--color-text-inverse);
29
+ }
30
+
31
+ .primary:hover:not(:disabled) {
32
+ background-color: var(--color-intent-primary-hover);
33
+ }
34
+
35
+ .primary:active:not(:disabled) {
36
+ transform: scale(0.98);
37
+ }
38
+
39
+ .secondary {
40
+ background-color: var(--color-bg-surface);
41
+ color: var(--color-text-primary);
42
+ border: 1px solid var(--color-border-default);
43
+ }
44
+
45
+ .secondary:hover:not(:disabled) {
46
+ background-color: var(--color-bg-subtle);
47
+ border-color: var(--color-border-strong);
48
+ }
49
+
50
+ .secondary:active:not(:disabled) {
51
+ transform: scale(0.98);
52
+ }
53
+
54
+ .ghost {
55
+ background-color: transparent;
56
+ color: var(--color-text-primary);
57
+ }
58
+
59
+ .ghost:hover:not(:disabled) {
60
+ background-color: var(--color-bg-subtle);
61
+ }
62
+
63
+ .ghost:active:not(:disabled) {
64
+ background-color: var(--color-bg-muted);
65
+ }
66
+
67
+ .danger {
68
+ background-color: var(--color-intent-danger);
69
+ color: var(--color-white);
70
+ }
71
+
72
+ .danger:hover:not(:disabled) {
73
+ background-color: var(--color-intent-danger-hover);
74
+ }
75
+
76
+ .danger:active:not(:disabled) {
77
+ transform: scale(0.98);
78
+ }
79
+
80
+ /* Sizes */
81
+ .sm {
82
+ height: 32px;
83
+ padding: 0 var(--space-3);
84
+ font-size: var(--text-sm);
85
+ }
86
+
87
+ .md {
88
+ height: 40px;
89
+ padding: 0 var(--button-padding-x);
90
+ font-size: var(--text-sm);
91
+ }
92
+
93
+ .lg {
94
+ height: 48px;
95
+ padding: 0 var(--space-6);
96
+ font-size: var(--text-base);
97
+ }
98
+
99
+ /* Loading State */
100
+ .spinner {
101
+ width: 16px;
102
+ height: 16px;
103
+ border: 2px solid currentColor;
104
+ border-right-color: transparent;
105
+ border-radius: 50%;
106
+ animation: spin 0.6s linear infinite;
107
+ }
108
+
109
+ .loadingText {
110
+ opacity: 0.7;
111
+ }
112
+
113
+ @keyframes spin {
114
+ to {
115
+ transform: rotate(360deg);
116
+ }
117
+ }
118
+
@@ -0,0 +1,108 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Button } from "./Button";
3
+
4
+ const meta: Meta<typeof Button> = {
5
+ title: "Components/Button",
6
+ component: Button,
7
+ parameters: {
8
+ layout: "centered",
9
+ },
10
+ tags: ["autodocs"],
11
+ argTypes: {
12
+ variant: {
13
+ control: "select",
14
+ options: ["primary", "secondary", "ghost", "danger"],
15
+ },
16
+ size: {
17
+ control: "select",
18
+ options: ["sm", "md", "lg"],
19
+ },
20
+ loading: {
21
+ control: "boolean",
22
+ },
23
+ disabled: {
24
+ control: "boolean",
25
+ },
26
+ },
27
+ };
28
+
29
+ export default meta;
30
+ type Story = StoryObj<typeof meta>;
31
+
32
+ export const Primary: Story = {
33
+ args: {
34
+ children: "Primary Button",
35
+ variant: "primary",
36
+ },
37
+ };
38
+
39
+ export const Secondary: Story = {
40
+ args: {
41
+ children: "Secondary Button",
42
+ variant: "secondary",
43
+ },
44
+ };
45
+
46
+ export const Ghost: Story = {
47
+ args: {
48
+ children: "Ghost Button",
49
+ variant: "ghost",
50
+ },
51
+ };
52
+
53
+ export const Danger: Story = {
54
+ args: {
55
+ children: "Delete",
56
+ variant: "danger",
57
+ },
58
+ };
59
+
60
+ export const Small: Story = {
61
+ args: {
62
+ children: "Small",
63
+ size: "sm",
64
+ },
65
+ };
66
+
67
+ export const Large: Story = {
68
+ args: {
69
+ children: "Large Button",
70
+ size: "lg",
71
+ },
72
+ };
73
+
74
+ export const Loading: Story = {
75
+ args: {
76
+ children: "Loading...",
77
+ loading: true,
78
+ },
79
+ };
80
+
81
+ export const Disabled: Story = {
82
+ args: {
83
+ children: "Disabled",
84
+ disabled: true,
85
+ },
86
+ };
87
+
88
+ export const AllVariants: Story = {
89
+ render: () => (
90
+ <div style={{ display: "flex", gap: "12px", flexWrap: "wrap" }}>
91
+ <Button variant="primary">Primary</Button>
92
+ <Button variant="secondary">Secondary</Button>
93
+ <Button variant="ghost">Ghost</Button>
94
+ <Button variant="danger">Danger</Button>
95
+ </div>
96
+ ),
97
+ };
98
+
99
+ export const AllSizes: Story = {
100
+ render: () => (
101
+ <div style={{ display: "flex", gap: "12px", alignItems: "center" }}>
102
+ <Button size="sm">Small</Button>
103
+ <Button size="md">Medium</Button>
104
+ <Button size="lg">Large</Button>
105
+ </div>
106
+ ),
107
+ };
108
+
@@ -0,0 +1,73 @@
1
+ import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { clsx } from "clsx";
5
+ import styles from "./Button.module.css";
6
+
7
+ const buttonVariants = cva(styles.button, {
8
+ variants: {
9
+ variant: {
10
+ primary: styles.primary,
11
+ secondary: styles.secondary,
12
+ ghost: styles.ghost,
13
+ danger: styles.danger,
14
+ },
15
+ size: {
16
+ sm: styles.sm,
17
+ md: styles.md,
18
+ lg: styles.lg,
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ variant: "primary",
23
+ size: "md",
24
+ },
25
+ });
26
+
27
+ export interface ButtonProps
28
+ extends ButtonHTMLAttributes<HTMLButtonElement>,
29
+ VariantProps<typeof buttonVariants> {
30
+ children: ReactNode;
31
+ loading?: boolean;
32
+ asChild?: boolean;
33
+ }
34
+
35
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
36
+ (
37
+ {
38
+ className,
39
+ variant,
40
+ size,
41
+ loading,
42
+ disabled,
43
+ asChild = false,
44
+ children,
45
+ ...props
46
+ },
47
+ ref
48
+ ) => {
49
+ const Comp = asChild ? Slot : "button";
50
+
51
+ return (
52
+ <Comp
53
+ className={clsx(buttonVariants({ variant, size }), className)}
54
+ ref={ref}
55
+ disabled={disabled || loading}
56
+ aria-disabled={disabled || loading}
57
+ {...props}
58
+ >
59
+ {loading ? (
60
+ <>
61
+ <span className={styles.spinner} aria-hidden="true" />
62
+ <span className={styles.loadingText}>{children}</span>
63
+ </>
64
+ ) : (
65
+ children
66
+ )}
67
+ </Comp>
68
+ );
69
+ }
70
+ );
71
+
72
+ Button.displayName = "Button";
73
+
@@ -0,0 +1,2 @@
1
+ export { Button, type ButtonProps } from "./Button";
2
+
@@ -0,0 +1,59 @@
1
+ .card {
2
+ background-color: var(--color-bg-surface);
3
+ border: 1px solid var(--color-border-default);
4
+ border-radius: var(--card-radius);
5
+ }
6
+
7
+ .elevated {
8
+ border-color: transparent;
9
+ box-shadow: var(--shadow-md);
10
+ }
11
+
12
+ /* Padding Variants */
13
+ .padding-none {
14
+ padding: 0;
15
+ }
16
+
17
+ .padding-sm {
18
+ padding: var(--space-3);
19
+ }
20
+
21
+ .padding-md {
22
+ padding: var(--card-padding);
23
+ }
24
+
25
+ .padding-lg {
26
+ padding: var(--space-6);
27
+ }
28
+
29
+ /* Subcomponents */
30
+ .header {
31
+ padding: var(--card-padding);
32
+ border-bottom: 1px solid var(--color-border-subtle);
33
+ }
34
+
35
+ .header:first-child {
36
+ border-top-left-radius: var(--card-radius);
37
+ border-top-right-radius: var(--card-radius);
38
+ }
39
+
40
+ .content {
41
+ padding: var(--card-padding);
42
+ }
43
+
44
+ .footer {
45
+ padding: var(--card-padding);
46
+ border-top: 1px solid var(--color-border-subtle);
47
+ background-color: var(--color-bg-subtle);
48
+ }
49
+
50
+ .footer:last-child {
51
+ border-bottom-left-radius: var(--card-radius);
52
+ border-bottom-right-radius: var(--card-radius);
53
+ }
54
+
55
+ /* When using subcomponents, remove padding from card itself */
56
+ .card:has(.header, .content, .footer) {
57
+ padding: 0;
58
+ }
59
+