@sethlivingston/cathode 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.
@@ -0,0 +1,4 @@
1
+ The IBM Plex font files in this directory (IBM Plex Sans, IBM Plex Mono)
2
+ are copyright IBM Corp. and licensed under the SIL Open Font License 1.1.
3
+ Full license text: https://github.com/IBM/plex/blob/master/LICENSE.txt
4
+ Files sourced via Fontsource (https://fontsource.org), latin subset, woff2.
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@sethlivingston/cathode",
3
+ "version": "0.1.0",
4
+ "description": "Dark-only CSS design system: inky surfaces, hairline borders, mono for data, arcade semantic color. Built to be driven by AI agents.",
5
+ "license": "MIT",
6
+ "author": "Seth Livingston",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/sethlivingston/cathode.git"
10
+ },
11
+ "keywords": [
12
+ "css",
13
+ "design-system",
14
+ "design-tokens",
15
+ "dark-theme",
16
+ "ai-agents"
17
+ ],
18
+ "style": "dist/cathode.css",
19
+ "exports": {
20
+ ".": "./dist/cathode.css",
21
+ "./tokens": "./src/tokens.css",
22
+ "./agents": "./AGENTS.md",
23
+ "./fonts/*": "./fonts/*",
24
+ "./src/*": "./src/*"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "fonts",
30
+ "AGENTS.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "node build.mjs"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
38
+ }
package/src/base.css ADDED
@@ -0,0 +1,166 @@
1
+ /* cathode/base.css — element defaults on the dark base */
2
+
3
+ html {
4
+ background: var(--ct-bg-0);
5
+ }
6
+
7
+ body {
8
+ font-family: var(--ct-font-sans);
9
+ font-size: var(--ct-text-md);
10
+ line-height: var(--ct-leading);
11
+ color: var(--ct-text);
12
+ background: var(--ct-bg-0);
13
+ -webkit-font-smoothing: antialiased;
14
+ }
15
+
16
+ ::selection {
17
+ background: var(--ct-accent);
18
+ color: var(--ct-on-accent);
19
+ }
20
+
21
+ /* Headings — tight, confident, no decoration */
22
+ h1,
23
+ h2,
24
+ h3,
25
+ h4,
26
+ h5,
27
+ h6 {
28
+ margin: 0 0 var(--ct-space-3);
29
+ line-height: var(--ct-leading-tight);
30
+ font-weight: 600;
31
+ letter-spacing: -0.01em;
32
+ }
33
+
34
+ h1 { font-size: var(--ct-text-3xl); }
35
+ h2 { font-size: var(--ct-text-2xl); }
36
+ h3 { font-size: var(--ct-text-xl); }
37
+ h4 { font-size: var(--ct-text-lg); }
38
+ h5,
39
+ h6 { font-size: var(--ct-text-md); }
40
+
41
+ p {
42
+ margin: 0 0 var(--ct-space-3);
43
+ }
44
+
45
+ p:last-child {
46
+ margin-bottom: 0;
47
+ }
48
+
49
+ a {
50
+ color: var(--ct-accent);
51
+ text-decoration: none;
52
+ transition: color var(--ct-quick) var(--ct-ease);
53
+ }
54
+
55
+ a:hover {
56
+ color: var(--ct-accent-bright);
57
+ text-decoration: underline;
58
+ text-underline-offset: 3px;
59
+ }
60
+
61
+ hr {
62
+ border: 0;
63
+ border-top: 1px solid var(--ct-border);
64
+ margin: var(--ct-space-5) 0;
65
+ }
66
+
67
+ ul,
68
+ ol {
69
+ margin: 0 0 var(--ct-space-3);
70
+ padding-left: var(--ct-space-5);
71
+ }
72
+
73
+ blockquote {
74
+ margin: 0 0 var(--ct-space-3);
75
+ padding: var(--ct-space-2) var(--ct-space-4);
76
+ border-left: 2px solid var(--ct-accent);
77
+ color: var(--ct-text-dim);
78
+ }
79
+
80
+ /* Code & data — the mono wink */
81
+ code,
82
+ kbd,
83
+ samp,
84
+ pre {
85
+ font-family: var(--ct-font-mono);
86
+ font-size: var(--ct-text-sm);
87
+ }
88
+
89
+ code,
90
+ samp {
91
+ background: var(--ct-bg-2);
92
+ border: 1px solid var(--ct-border);
93
+ border-radius: var(--ct-radius);
94
+ padding: 1px 5px;
95
+ }
96
+
97
+ pre {
98
+ background: var(--ct-bg-1);
99
+ border: 1px solid var(--ct-border);
100
+ border-radius: var(--ct-radius);
101
+ padding: var(--ct-space-4);
102
+ overflow-x: auto;
103
+ margin: 0 0 var(--ct-space-3);
104
+ }
105
+
106
+ pre code {
107
+ background: none;
108
+ border: 0;
109
+ padding: 0;
110
+ }
111
+
112
+ kbd {
113
+ background: var(--ct-bg-3);
114
+ border: 1px solid var(--ct-border-bright);
115
+ border-bottom-width: 2px;
116
+ border-radius: var(--ct-radius);
117
+ padding: 1px 6px;
118
+ font-size: var(--ct-text-xs);
119
+ }
120
+
121
+ /* .ct-data — apply to any inline run of IDs, counts, timestamps */
122
+ .ct-data {
123
+ font-family: var(--ct-font-mono);
124
+ font-size: var(--ct-text-sm);
125
+ font-variant-numeric: tabular-nums;
126
+ }
127
+
128
+ /* .ct-label-mono — uppercase mono micro-label (section headers, table heads) */
129
+ .ct-label-mono {
130
+ font-family: var(--ct-font-mono);
131
+ font-size: var(--ct-text-xs);
132
+ font-weight: 600;
133
+ letter-spacing: var(--ct-tracking-mono);
134
+ text-transform: uppercase;
135
+ color: var(--ct-text-faint);
136
+ }
137
+
138
+ /* Muted text helpers */
139
+ .ct-dim { color: var(--ct-text-dim); }
140
+ .ct-faint { color: var(--ct-text-faint); }
141
+
142
+ /* Focus — one ring to rule them all */
143
+ :focus-visible {
144
+ outline: 2px solid var(--ct-accent-glow);
145
+ outline-offset: 1px;
146
+ }
147
+
148
+ /* Scrollbars — quiet, square */
149
+ * {
150
+ scrollbar-width: thin;
151
+ scrollbar-color: var(--ct-border-bright) transparent;
152
+ }
153
+
154
+ *::-webkit-scrollbar {
155
+ width: 10px;
156
+ height: 10px;
157
+ }
158
+
159
+ *::-webkit-scrollbar-thumb {
160
+ background: var(--ct-border-bright);
161
+ border: 2px solid var(--ct-bg-0);
162
+ }
163
+
164
+ *::-webkit-scrollbar-track {
165
+ background: transparent;
166
+ }
@@ -0,0 +1,59 @@
1
+ /* cathode/badge.css — the arcade tag. Always mono, always meaningful.
2
+ Status variants have fixed meanings; categorical variants are free.
3
+ Custom hue: <span class="ct-badge" style="--badge-hue: var(--ct-pink)"> */
4
+
5
+ .ct-badge {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ gap: var(--ct-space-1);
9
+ font-family: var(--ct-font-mono);
10
+ font-size: var(--ct-text-xs);
11
+ font-weight: 600;
12
+ letter-spacing: var(--ct-tracking-mono);
13
+ text-transform: uppercase;
14
+ line-height: 1;
15
+ border-radius: var(--ct-radius);
16
+ padding: 3px var(--ct-space-2);
17
+ --badge-hue: var(--ct-text-dim);
18
+ color: var(--badge-hue);
19
+ background: color-mix(in srgb, var(--badge-hue) 13%, transparent);
20
+ border: 1px solid color-mix(in srgb, var(--badge-hue) 35%, transparent);
21
+ white-space: nowrap;
22
+ }
23
+
24
+ /* Status — fixed meanings */
25
+ .ct-badge--success { --badge-hue: var(--ct-green); }
26
+ .ct-badge--danger { --badge-hue: var(--ct-red); }
27
+ .ct-badge--warning { --badge-hue: var(--ct-amber); }
28
+ .ct-badge--info { --badge-hue: var(--ct-cyan); }
29
+ .ct-badge--accent { --badge-hue: var(--ct-accent); }
30
+
31
+ /* Categorical — free for tags/series */
32
+ .ct-badge--orange { --badge-hue: var(--ct-orange); }
33
+ .ct-badge--lime { --badge-hue: var(--ct-lime); }
34
+ .ct-badge--pink { --badge-hue: var(--ct-pink); }
35
+ .ct-badge--indigo { --badge-hue: var(--ct-indigo); }
36
+
37
+ /* Solid variant — louder, for counts and hero stats */
38
+ .ct-badge--solid {
39
+ color: var(--ct-bg-0);
40
+ background: var(--badge-hue);
41
+ border-color: var(--badge-hue);
42
+ }
43
+
44
+ /* Status dot — minimal presence indicator */
45
+ .ct-dot {
46
+ display: inline-block;
47
+ width: 7px;
48
+ height: 7px;
49
+ border-radius: 50%;
50
+ --dot-hue: var(--ct-text-faint);
51
+ background: var(--dot-hue);
52
+ box-shadow: 0 0 6px color-mix(in srgb, var(--dot-hue) 60%, transparent);
53
+ }
54
+
55
+ .ct-dot--success { --dot-hue: var(--ct-green); }
56
+ .ct-dot--danger { --dot-hue: var(--ct-red); }
57
+ .ct-dot--warning { --dot-hue: var(--ct-amber); }
58
+ .ct-dot--info { --dot-hue: var(--ct-cyan); }
59
+ .ct-dot--accent { --dot-hue: var(--ct-accent); }
@@ -0,0 +1,107 @@
1
+ /* cathode/button.css */
2
+
3
+ .ct-button {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ gap: var(--ct-space-2);
8
+ font-family: var(--ct-font-sans);
9
+ font-size: var(--ct-text-md);
10
+ font-weight: 500;
11
+ line-height: 1;
12
+ color: var(--ct-text);
13
+ background: var(--ct-bg-2);
14
+ border: 1px solid var(--ct-border);
15
+ border-radius: var(--ct-radius);
16
+ padding: var(--ct-space-2) var(--ct-space-4);
17
+ min-height: 36px;
18
+ cursor: pointer;
19
+ user-select: none;
20
+ white-space: nowrap;
21
+ transition:
22
+ border-color var(--ct-quick) var(--ct-ease),
23
+ background var(--ct-quick) var(--ct-ease),
24
+ color var(--ct-quick) var(--ct-ease);
25
+ }
26
+
27
+ .ct-button:hover {
28
+ border-color: var(--ct-border-bright);
29
+ background: var(--ct-bg-3);
30
+ }
31
+
32
+ .ct-button:active {
33
+ transform: translateY(1px);
34
+ }
35
+
36
+ .ct-button:disabled {
37
+ opacity: 0.45;
38
+ cursor: not-allowed;
39
+ transform: none;
40
+ }
41
+
42
+ /* Primary — the one magenta action per view */
43
+ .ct-button--primary {
44
+ background: var(--ct-accent);
45
+ border-color: var(--ct-accent);
46
+ color: var(--ct-on-accent);
47
+ font-weight: 600;
48
+ }
49
+
50
+ .ct-button--primary:hover {
51
+ background: var(--ct-accent-bright);
52
+ border-color: var(--ct-accent-bright);
53
+ }
54
+
55
+ /* Danger — destructive actions only */
56
+ .ct-button--danger {
57
+ color: var(--ct-red);
58
+ border-color: color-mix(in srgb, var(--ct-red) 40%, transparent);
59
+ }
60
+
61
+ .ct-button--danger:hover {
62
+ background: color-mix(in srgb, var(--ct-red) 12%, transparent);
63
+ border-color: var(--ct-red);
64
+ }
65
+
66
+ /* Ghost — toolbar/inline actions, no box until hover */
67
+ .ct-button--ghost {
68
+ background: transparent;
69
+ border-color: transparent;
70
+ color: var(--ct-text-dim);
71
+ }
72
+
73
+ .ct-button--ghost:hover {
74
+ background: var(--ct-bg-2);
75
+ border-color: var(--ct-border);
76
+ color: var(--ct-text);
77
+ }
78
+
79
+ /* Sizes */
80
+ .ct-button--sm {
81
+ font-size: var(--ct-text-sm);
82
+ padding: var(--ct-space-1) var(--ct-space-3);
83
+ min-height: 28px;
84
+ }
85
+
86
+ .ct-button--lg {
87
+ font-size: var(--ct-text-lg);
88
+ padding: var(--ct-space-3) var(--ct-space-5);
89
+ min-height: 44px;
90
+ }
91
+
92
+ /* Icon-only — square */
93
+ .ct-button--icon {
94
+ padding: var(--ct-space-2);
95
+ width: 36px;
96
+ }
97
+
98
+ .ct-button--icon.ct-button--sm {
99
+ width: 28px;
100
+ padding: var(--ct-space-1);
101
+ }
102
+
103
+ /* Button row */
104
+ .ct-button-group {
105
+ display: inline-flex;
106
+ gap: var(--ct-space-2);
107
+ }
@@ -0,0 +1,68 @@
1
+ /* cathode/card.css — the 1px-ruled box. Elevation = border brightness. */
2
+
3
+ .ct-card {
4
+ background: var(--ct-bg-1);
5
+ border: 1px solid var(--ct-border);
6
+ border-radius: var(--ct-radius);
7
+ }
8
+
9
+ .ct-card--interactive {
10
+ cursor: pointer;
11
+ transition: border-color var(--ct-quick) var(--ct-ease);
12
+ }
13
+
14
+ .ct-card--interactive:hover {
15
+ border-color: var(--ct-border-bright);
16
+ }
17
+
18
+ .ct-card__header {
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: space-between;
22
+ gap: var(--ct-space-3);
23
+ padding: var(--ct-space-3) var(--ct-space-4);
24
+ border-bottom: 1px solid var(--ct-border);
25
+ }
26
+
27
+ .ct-card__title {
28
+ font-size: var(--ct-text-md);
29
+ font-weight: 600;
30
+ margin: 0;
31
+ }
32
+
33
+ .ct-card__body {
34
+ padding: var(--ct-space-4);
35
+ }
36
+
37
+ .ct-card__footer {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: flex-end;
41
+ gap: var(--ct-space-2);
42
+ padding: var(--ct-space-3) var(--ct-space-4);
43
+ border-top: 1px solid var(--ct-border);
44
+ }
45
+
46
+ /* Stat card — hero number in mono */
47
+ .ct-stat {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: var(--ct-space-1);
51
+ padding: var(--ct-space-4);
52
+ }
53
+
54
+ .ct-stat__value {
55
+ font-family: var(--ct-font-mono);
56
+ font-size: var(--ct-text-2xl);
57
+ font-weight: 600;
58
+ font-variant-numeric: tabular-nums;
59
+ line-height: var(--ct-leading-tight);
60
+ }
61
+
62
+ .ct-stat__delta {
63
+ font-family: var(--ct-font-mono);
64
+ font-size: var(--ct-text-sm);
65
+ }
66
+
67
+ .ct-stat__delta--up { color: var(--ct-green); }
68
+ .ct-stat__delta--down { color: var(--ct-red); }
@@ -0,0 +1,115 @@
1
+ /* cathode/feedback.css — progress, meter, spinner, empty state, alert */
2
+
3
+ /* Progress bar — smooth fill. Set width on the fill span. */
4
+ .ct-progress {
5
+ height: 6px;
6
+ background: var(--ct-bg-3);
7
+ border: 1px solid var(--ct-border);
8
+ border-radius: var(--ct-radius);
9
+ overflow: hidden;
10
+ }
11
+
12
+ .ct-progress__fill {
13
+ display: block;
14
+ height: 100%;
15
+ background: var(--progress-hue, var(--ct-accent));
16
+ transition: width var(--ct-slow) var(--ct-ease);
17
+ }
18
+
19
+ .ct-progress--success { --progress-hue: var(--ct-green); }
20
+ .ct-progress--danger { --progress-hue: var(--ct-red); }
21
+ .ct-progress--warning { --progress-hue: var(--ct-amber); }
22
+
23
+ /* Meter — the cell variant. Visible segments, the one louder wink. */
24
+ .ct-meter {
25
+ height: 10px;
26
+ background: var(--ct-bg-3);
27
+ border: 1px solid var(--ct-border);
28
+ border-radius: var(--ct-radius);
29
+ overflow: hidden;
30
+ }
31
+
32
+ .ct-meter__fill {
33
+ display: block;
34
+ height: 100%;
35
+ background: var(--progress-hue, var(--ct-accent));
36
+ -webkit-mask: repeating-linear-gradient(
37
+ to right,
38
+ #000 0 6px,
39
+ transparent 6px 8px
40
+ );
41
+ mask: repeating-linear-gradient(to right, #000 0 6px, transparent 6px 8px);
42
+ transition: width var(--ct-slow) var(--ct-ease);
43
+ }
44
+
45
+ /* Spinner — a rotating square, naturally */
46
+ .ct-spinner {
47
+ display: inline-block;
48
+ width: 14px;
49
+ height: 14px;
50
+ border: 2px solid var(--ct-border-bright);
51
+ border-top-color: var(--ct-accent);
52
+ border-radius: var(--ct-radius);
53
+ animation: ct-spin 0.7s linear infinite;
54
+ }
55
+
56
+ @keyframes ct-spin {
57
+ to {
58
+ transform: rotate(360deg);
59
+ }
60
+ }
61
+
62
+ /* Empty state */
63
+ .ct-empty {
64
+ display: flex;
65
+ flex-direction: column;
66
+ align-items: center;
67
+ justify-content: center;
68
+ gap: var(--ct-space-2);
69
+ padding: var(--ct-space-7) var(--ct-space-4);
70
+ text-align: center;
71
+ border: 1px dashed var(--ct-border-bright);
72
+ border-radius: var(--ct-radius);
73
+ color: var(--ct-text-dim);
74
+ }
75
+
76
+ .ct-empty__glyph {
77
+ font-family: var(--ct-font-mono);
78
+ font-size: var(--ct-text-2xl);
79
+ color: var(--ct-text-faint);
80
+ line-height: 1;
81
+ }
82
+
83
+ .ct-empty__title {
84
+ font-weight: 600;
85
+ color: var(--ct-text);
86
+ }
87
+
88
+ .ct-empty__hint {
89
+ font-size: var(--ct-text-sm);
90
+ color: var(--ct-text-faint);
91
+ max-width: 360px;
92
+ }
93
+
94
+ /* Alert — inline callout */
95
+ .ct-alert {
96
+ display: flex;
97
+ gap: var(--ct-space-3);
98
+ padding: var(--ct-space-3) var(--ct-space-4);
99
+ font-size: var(--ct-text-md);
100
+ border: 1px solid color-mix(in srgb, var(--alert-hue, var(--ct-cyan)) 35%, transparent);
101
+ border-left-width: 2px;
102
+ border-left-color: var(--alert-hue, var(--ct-cyan));
103
+ border-radius: var(--ct-radius);
104
+ background: color-mix(in srgb, var(--alert-hue, var(--ct-cyan)) 7%, transparent);
105
+ }
106
+
107
+ .ct-alert--success { --alert-hue: var(--ct-green); }
108
+ .ct-alert--danger { --alert-hue: var(--ct-red); }
109
+ .ct-alert--warning { --alert-hue: var(--ct-amber); }
110
+ .ct-alert--info { --alert-hue: var(--ct-cyan); }
111
+
112
+ .ct-alert__title {
113
+ font-weight: 600;
114
+ margin: 0 0 var(--ct-space-1);
115
+ }