canny-ui 1.0.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,132 @@
1
+ @use '../tokens/primitives' as *;
2
+
3
+ // ── Text / Typography primitives ─────────────────────────────────────────
4
+ // Consumed by the React <Text> component. Each variant is a self-contained
5
+ // typographic style; the component picks the default element (h1, p, etc.)
6
+ // but consumers can override via `as`.
7
+
8
+ .cny-text {
9
+ font-family: var(--font-body);
10
+ color: var(--text-primary);
11
+ margin: 0;
12
+ }
13
+
14
+ .cny-text-display {
15
+ font-family: var(--font-display);
16
+ font-size: clamp(2.5rem, 5vw, 4rem);
17
+ font-weight: 500;
18
+ line-height: 1.05;
19
+ letter-spacing: -0.03em;
20
+ }
21
+
22
+ .cny-text-h1 {
23
+ font-family: var(--font-display);
24
+ font-size: clamp(1.875rem, 3.2vw, 2.5rem);
25
+ font-weight: 500;
26
+ line-height: 1.15;
27
+ letter-spacing: -0.025em;
28
+ }
29
+
30
+ .cny-text-h2 {
31
+ font-family: var(--font-display);
32
+ font-size: clamp(1.5rem, 2.4vw, 1.875rem);
33
+ font-weight: 500;
34
+ line-height: 1.2;
35
+ letter-spacing: -0.02em;
36
+ }
37
+
38
+ .cny-text-h3 {
39
+ font-family: var(--font-body);
40
+ font-size: 1.25rem;
41
+ font-weight: 600;
42
+ line-height: 1.3;
43
+ letter-spacing: -0.015em;
44
+ }
45
+
46
+ .cny-text-h4 {
47
+ font-family: var(--font-body);
48
+ font-size: 1.0625rem;
49
+ font-weight: 600;
50
+ line-height: 1.35;
51
+ letter-spacing: -0.01em;
52
+ }
53
+
54
+ .cny-text-h5 {
55
+ font-family: var(--font-body);
56
+ font-size: 0.9375rem;
57
+ font-weight: 600;
58
+ line-height: 1.4;
59
+ letter-spacing: -0.005em;
60
+ }
61
+
62
+ .cny-text-h6 {
63
+ font-family: var(--font-body);
64
+ font-size: 0.8125rem;
65
+ font-weight: 600;
66
+ line-height: 1.4;
67
+ letter-spacing: var(--track-wide);
68
+ text-transform: uppercase;
69
+ color: var(--text-muted);
70
+ }
71
+
72
+ .cny-text-lead {
73
+ font-family: var(--font-body);
74
+ font-size: 1.125rem;
75
+ font-weight: 400;
76
+ line-height: 1.55;
77
+ color: var(--text-secondary);
78
+ }
79
+
80
+ .cny-text-body {
81
+ font-family: var(--font-body);
82
+ font-size: 0.9375rem;
83
+ font-weight: 400;
84
+ line-height: 1.6;
85
+ }
86
+
87
+ .cny-text-small {
88
+ font-family: var(--font-body);
89
+ font-size: 0.8125rem;
90
+ font-weight: 400;
91
+ line-height: 1.5;
92
+ color: var(--text-secondary);
93
+ }
94
+
95
+ .cny-text-caption {
96
+ font-family: var(--font-body);
97
+ font-size: 0.75rem;
98
+ font-weight: 400;
99
+ line-height: 1.5;
100
+ color: var(--text-muted);
101
+ }
102
+
103
+ .cny-text-code {
104
+ font-family: var(--font-mono);
105
+ font-size: 0.8125rem;
106
+ padding: 0.1em 0.35em;
107
+ border-radius: var(--radius-xs);
108
+ background: var(--bg-surface-low);
109
+ color: var(--text-primary);
110
+ }
111
+
112
+ .cny-text-kbd {
113
+ font-family: var(--font-mono);
114
+ font-size: 0.75rem;
115
+ padding: 0.1em 0.4em;
116
+ border-radius: var(--radius-xs);
117
+ background: var(--bg-surface-lowest);
118
+ color: var(--text-primary);
119
+ box-shadow: inset 0 0 0 1px var(--border-color), 0 1px 0 0 var(--border-color);
120
+ }
121
+
122
+ // ── Weight modifiers ─────────────────────────────────────────────────────
123
+ .cny-weight-regular { font-weight: 400; }
124
+ .cny-weight-medium { font-weight: 500; }
125
+ .cny-weight-semibold { font-weight: 600; }
126
+ .cny-weight-bold { font-weight: 700; }
127
+
128
+ // ── Alignment ────────────────────────────────────────────────────────────
129
+ .cny-text-left { text-align: left; }
130
+ .cny-text-center { text-align: center; }
131
+ .cny-text-right { text-align: right; }
132
+ .cny-text-justify { text-align: justify; }
@@ -0,0 +1,72 @@
1
+ @use '../tokens/primitives' as *;
2
+
3
+ // Tooltip — CSS-only, powered by `data-tooltip` attribute on the trigger.
4
+ // Usage: <button class="has-tooltip" data-tooltip="Hello">?</button>
5
+
6
+ .has-tooltip {
7
+ position: relative;
8
+
9
+ &::before,
10
+ &::after {
11
+ position: absolute;
12
+ left: 50%;
13
+ opacity: 0;
14
+ pointer-events: none;
15
+ transition:
16
+ opacity var(--transition-fast),
17
+ transform var(--transition-fast);
18
+ z-index: 50;
19
+ }
20
+
21
+ &::before {
22
+ content: attr(data-tooltip);
23
+ bottom: calc(100% + 8px);
24
+ transform: translate(-50%, 4px);
25
+ padding: 6px 10px;
26
+ background: var(--color-neutral);
27
+ color: var(--color-neutral-on);
28
+ font-family: var(--font-body);
29
+ font-size: 0.75rem;
30
+ font-weight: 500;
31
+ line-height: 1.3;
32
+ white-space: nowrap;
33
+ border-radius: var(--radius-sm);
34
+ box-shadow: var(--shadow-md);
35
+ max-width: 240px;
36
+ }
37
+
38
+ &::after {
39
+ content: '';
40
+ bottom: calc(100% + 2px);
41
+ transform: translate(-50%, 4px);
42
+ border: 4px solid transparent;
43
+ border-top-color: var(--color-neutral);
44
+ }
45
+
46
+ &:hover::before,
47
+ &:hover::after,
48
+ &:focus-visible::before,
49
+ &:focus-visible::after {
50
+ opacity: 1;
51
+ transform: translate(-50%, 0);
52
+ }
53
+
54
+ // Position variants
55
+ &.tooltip-bottom {
56
+ &::before {
57
+ bottom: auto;
58
+ top: calc(100% + 8px);
59
+ }
60
+ &::after {
61
+ bottom: auto;
62
+ top: calc(100% + 2px);
63
+ border-top-color: transparent;
64
+ border-bottom-color: var(--color-neutral);
65
+ }
66
+ }
67
+ }
68
+
69
+ @media (prefers-reduced-motion: reduce) {
70
+ .has-tooltip::before,
71
+ .has-tooltip::after { transition: opacity var(--transition-fast); }
72
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Canny UI — table column resizer.
3
+ * Attaches drag handles to every `.table.resizable` on the page.
4
+ *
5
+ * Usage:
6
+ * <script src="canny-ui/dist/table-resize.js" defer></script>
7
+ * <table class="table resizable"> ... </table>
8
+ *
9
+ * Or, for dynamically inserted tables:
10
+ * window.CannyUI.makeTableResizable(tableEl);
11
+ */
12
+ (function () {
13
+ var MIN_WIDTH = 40;
14
+
15
+ function measureAndFix(table, ths) {
16
+ // Measure under auto layout, then freeze with table-layout: fixed.
17
+ var widths = [];
18
+ for (var i = 0; i < ths.length; i++) widths.push(ths[i].offsetWidth);
19
+ table.style.tableLayout = 'fixed';
20
+ for (var j = 0; j < ths.length; j++) ths[j].style.width = widths[j] + 'px';
21
+ }
22
+
23
+ function makeResizable(table) {
24
+ if (table.dataset.resizableInit === '1') return;
25
+ var ths = table.querySelectorAll('thead th');
26
+ if (!ths.length) return;
27
+
28
+ measureAndFix(table, ths);
29
+ table.dataset.resizableInit = '1';
30
+
31
+ for (var i = 0; i < ths.length; i++) {
32
+ // Last column has nothing to drag against.
33
+ if (i === ths.length - 1) continue;
34
+ attachHandle(table, ths[i]);
35
+ }
36
+ }
37
+
38
+ function attachHandle(table, th) {
39
+ var resizer = th.querySelector('.th-resizer');
40
+ if (!resizer) {
41
+ resizer = document.createElement('span');
42
+ resizer.className = 'th-resizer';
43
+ resizer.setAttribute('aria-hidden', 'true');
44
+ th.appendChild(resizer);
45
+ }
46
+
47
+ function pointerDown(e) {
48
+ e.preventDefault();
49
+ e.stopPropagation();
50
+
51
+ var isTouch = e.type === 'touchstart';
52
+ var startX = isTouch ? e.touches[0].clientX : e.clientX;
53
+ var startWidth = th.offsetWidth;
54
+
55
+ resizer.classList.add('resizing');
56
+ table.classList.add('is-resizing');
57
+ document.body.style.cursor = 'col-resize';
58
+
59
+ function move(ev) {
60
+ var clientX = ev.touches ? ev.touches[0].clientX : ev.clientX;
61
+ var next = Math.max(MIN_WIDTH, startWidth + (clientX - startX));
62
+ th.style.width = next + 'px';
63
+ }
64
+
65
+ function stop() {
66
+ resizer.classList.remove('resizing');
67
+ table.classList.remove('is-resizing');
68
+ document.body.style.cursor = '';
69
+ document.removeEventListener('mousemove', move);
70
+ document.removeEventListener('mouseup', stop);
71
+ document.removeEventListener('touchmove', move);
72
+ document.removeEventListener('touchend', stop);
73
+ document.removeEventListener('touchcancel', stop);
74
+ }
75
+
76
+ document.addEventListener('mousemove', move);
77
+ document.addEventListener('mouseup', stop);
78
+ document.addEventListener('touchmove', move, { passive: false });
79
+ document.addEventListener('touchend', stop);
80
+ document.addEventListener('touchcancel', stop);
81
+ }
82
+
83
+ resizer.addEventListener('mousedown', pointerDown);
84
+ resizer.addEventListener('touchstart', pointerDown, { passive: false });
85
+ }
86
+
87
+ function init() {
88
+ var tables = document.querySelectorAll('.table.resizable');
89
+ for (var i = 0; i < tables.length; i++) makeResizable(tables[i]);
90
+ }
91
+
92
+ if (typeof document !== 'undefined') {
93
+ if (document.readyState === 'loading') {
94
+ document.addEventListener('DOMContentLoaded', init);
95
+ } else {
96
+ init();
97
+ }
98
+ }
99
+
100
+ if (typeof window !== 'undefined') {
101
+ window.CannyUI = window.CannyUI || {};
102
+ window.CannyUI.makeTableResizable = makeResizable;
103
+ }
104
+ })();
@@ -0,0 +1,73 @@
1
+ // Canny-UI primitive tokens — Enterprise palette.
2
+ // Zinc neutrals, indigo primary. Calibrated for trust, not fashion.
3
+ // Inspired by proven enterprise systems: Linear, Stripe, Vercel.
4
+
5
+ // ── Zinc — cool neutral scale (surfaces + text) ─────────────────────────
6
+ $zinc-50: #fafafa;
7
+ $zinc-100: #f4f4f5;
8
+ $zinc-200: #e4e4e7;
9
+ $zinc-300: #d4d4d8;
10
+ $zinc-400: #a1a1aa;
11
+ $zinc-500: #71717a;
12
+ $zinc-600: #52525b;
13
+ $zinc-700: #3f3f46;
14
+ $zinc-800: #27272a;
15
+ $zinc-900: #18181b;
16
+ $zinc-950: #09090b;
17
+
18
+ // ── Indigo — primary brand ──────────────────────────────────────────────
19
+ // #4f46e5 at 600 is the Linear/Vercel-adjacent "serious" indigo.
20
+ $indigo-50: #eef2ff;
21
+ $indigo-100: #e0e7ff;
22
+ $indigo-200: #c7d2fe;
23
+ $indigo-400: #818cf8;
24
+ $indigo-500: #6366f1;
25
+ $indigo-600: #4f46e5;
26
+ $indigo-700: #4338ca;
27
+ $indigo-800: #3730a3;
28
+
29
+ // ── Semantic — standard enterprise, not editorial ───────────────────────
30
+ $red-50: #fef2f2;
31
+ $red-500: #ef4444;
32
+ $red-600: #dc2626;
33
+ $red-700: #b91c1c;
34
+
35
+ $amber-50: #fffbeb;
36
+ $amber-500: #f59e0b;
37
+ $amber-600: #d97706;
38
+ $amber-700: #b45309;
39
+
40
+ $emerald-50: #ecfdf5;
41
+ $emerald-500: #10b981;
42
+ $emerald-600: #059669;
43
+ $emerald-700: #047857;
44
+
45
+ $sky-50: #f0f9ff;
46
+ $sky-500: #0ea5e9;
47
+ $sky-600: #0284c7;
48
+ $sky-700: #0369a1;
49
+
50
+ // ── Spacing — 4px grid ──────────────────────────────────────────────────
51
+ $space-0: 0;
52
+ $space-1: 4px;
53
+ $space-2: 8px;
54
+ $space-3: 12px;
55
+ $space-4: 16px;
56
+ $space-5: 20px;
57
+ $space-6: 24px;
58
+ $space-8: 32px;
59
+ $space-10: 40px;
60
+ $space-12: 48px;
61
+ $space-16: 64px;
62
+
63
+ // ── Radii — enterprise-conservative ─────────────────────────────────────
64
+ $radius-xs: 3px;
65
+ $radius-sm: 5px;
66
+ $radius-md: 7px;
67
+ $radius-lg: 10px;
68
+ $radius-xl: 14px;
69
+ $radius-full: 9999px;
70
+
71
+ // ── Grid & hairline ─────────────────────────────────────────────────────
72
+ $hairline: 1px;
73
+ $grid-unit: 4px;
@@ -0,0 +1,199 @@
1
+ @use 'primitives' as *;
2
+
3
+ :root {
4
+ // ── Surfaces ──────────────────────────────────────────────────────────
5
+ --bg-base: #{$zinc-50};
6
+ --bg-surface-low: #{$zinc-100};
7
+ --bg-surface-lowest: #ffffff;
8
+ --bg-surface-edge: #{$zinc-200};
9
+
10
+ // ── Text ──────────────────────────────────────────────────────────────
11
+ --text-primary: #{$zinc-950};
12
+ --text-secondary: #{$zinc-700};
13
+ --text-muted: #{$zinc-500};
14
+ --text-disabled: #{$zinc-400};
15
+ --text-on-solid: #ffffff;
16
+ --text-accent: #{$indigo-600};
17
+
18
+ // ── Brand (primary) ───────────────────────────────────────────────────
19
+ --color-primary: #{$indigo-600};
20
+ --color-primary-hover: #{$indigo-700};
21
+ --color-primary-active:#{$indigo-800};
22
+ --color-primary-soft: #{$indigo-50};
23
+ --color-primary-on: #ffffff;
24
+
25
+ // ── Neutral solid (secondary button) ──────────────────────────────────
26
+ --color-neutral: #{$zinc-900};
27
+ --color-neutral-hover: #{$zinc-800};
28
+ --color-neutral-soft: #{$zinc-100};
29
+ --color-neutral-on: #ffffff;
30
+
31
+ // ── Semantic — roles ──────────────────────────────────────────────────
32
+ --color-danger: #{$red-600};
33
+ --color-danger-hover: #{$red-700};
34
+ --color-danger-soft: #{$red-50};
35
+ --color-danger-on: #ffffff;
36
+
37
+ --color-warning: #{$amber-600};
38
+ --color-warning-hover: #{$amber-700};
39
+ --color-warning-soft: #{$amber-50};
40
+ --color-warning-on: #ffffff;
41
+
42
+ --color-success: #{$emerald-600};
43
+ --color-success-hover: #{$emerald-700};
44
+ --color-success-soft: #{$emerald-50};
45
+ --color-success-on: #ffffff;
46
+
47
+ --color-info: #{$sky-600};
48
+ --color-info-hover: #{$sky-700};
49
+ --color-info-soft: #{$sky-50};
50
+ --color-info-on: #ffffff;
51
+
52
+ // ── Borders (enterprise uses real borders, unlike the paper edition) ──
53
+ --border-color: #{$zinc-200};
54
+ --border-color-strong: #{$zinc-300};
55
+ --border-focus: #{$indigo-500};
56
+
57
+ // ── Ring (focus halo) ─────────────────────────────────────────────────
58
+ --ring-color: rgba(79, 70, 229, 0.28);
59
+ --ring-width: 3px;
60
+
61
+ // ── Shadows — subtle, predictable ─────────────────────────────────────
62
+ --shadow-xs: 0 1px 2px 0 rgba(9, 9, 11, 0.05);
63
+ --shadow-sm: 0 1px 2px 0 rgba(9, 9, 11, 0.06),
64
+ 0 1px 3px 0 rgba(9, 9, 11, 0.04);
65
+ --shadow-md: 0 4px 6px -1px rgba(9, 9, 11, 0.07),
66
+ 0 2px 4px -2px rgba(9, 9, 11, 0.05);
67
+ --shadow-lg: 0 10px 15px -3px rgba(9, 9, 11, 0.08),
68
+ 0 4px 6px -4px rgba(9, 9, 11, 0.05);
69
+
70
+ // Legacy aliases (kept for backward compatibility with prior demo)
71
+ --shadow-card: var(--shadow-sm);
72
+ --shadow-lift: var(--shadow-lg);
73
+ --shadow-ambient: 0 0 0 3px var(--ring-color);
74
+
75
+ // ── Motion ────────────────────────────────────────────────────────────
76
+ --ease-standard: cubic-bezier(0.2, 0, 0, 1);
77
+ --transition-fast: 120ms var(--ease-standard);
78
+ --transition-base: 180ms var(--ease-standard);
79
+ --transition-slow: 260ms var(--ease-standard);
80
+
81
+ // ── Radii ─────────────────────────────────────────────────────────────
82
+ --radius-xs: #{$radius-xs};
83
+ --radius-sm: #{$radius-sm};
84
+ --radius-md: #{$radius-md};
85
+ --radius-lg: #{$radius-lg};
86
+ --radius-xl: #{$radius-xl};
87
+ --radius-full: #{$radius-full};
88
+
89
+ // ── Fonts ─────────────────────────────────────────────────────────────
90
+ --font-body: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
91
+ --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
92
+ --font-display: 'Fraunces', Georgia, serif;
93
+
94
+ // ── Hairline (for dividers) ───────────────────────────────────────────
95
+ --hairline-color: #{$zinc-200};
96
+ --hairline-strong: #{$zinc-300};
97
+
98
+ // ── Type scale ────────────────────────────────────────────────────────
99
+ --text-xs: 0.75rem;
100
+ --text-sm: 0.875rem;
101
+ --text-base: 0.9375rem;
102
+ --text-lg: 1.0625rem;
103
+ --text-xl: 1.25rem;
104
+ --text-2xl: 1.5rem;
105
+ --text-3xl: 1.875rem;
106
+ --text-4xl: 2.25rem;
107
+
108
+ --track-tight: -0.015em;
109
+ --track-normal: 0;
110
+ --track-wide: 0.04em;
111
+ --track-wider: 0.08em;
112
+ }
113
+
114
+ // ── Dark theme ──────────────────────────────────────────────────────────
115
+ @media (prefers-color-scheme: dark) {
116
+ :root {
117
+ --bg-base: #{$zinc-950};
118
+ --bg-surface-low: #{$zinc-900};
119
+ --bg-surface-lowest: #{$zinc-800};
120
+ --bg-surface-edge: #{$zinc-700};
121
+
122
+ --text-primary: #{$zinc-50};
123
+ --text-secondary: #{$zinc-300};
124
+ --text-muted: #{$zinc-400};
125
+ --text-disabled: #{$zinc-600};
126
+ --text-accent: #{$indigo-400};
127
+
128
+ --color-primary: #{$indigo-500};
129
+ --color-primary-hover: #{$indigo-400};
130
+ --color-primary-active:#{$indigo-600};
131
+ --color-primary-soft: rgba(99, 102, 241, 0.15);
132
+
133
+ --color-neutral: #{$zinc-50};
134
+ --color-neutral-hover: #ffffff;
135
+ --color-neutral-soft: #{$zinc-800};
136
+ --color-neutral-on: #{$zinc-950};
137
+
138
+ --color-danger-soft: rgba(220, 38, 38, 0.16);
139
+ --color-warning-soft: rgba(217, 119, 6, 0.16);
140
+ --color-success-soft: rgba(5, 150, 105, 0.16);
141
+ --color-info-soft: rgba(2, 132, 199, 0.16);
142
+
143
+ --border-color: #{$zinc-800};
144
+ --border-color-strong: #{$zinc-700};
145
+ --border-focus: #{$indigo-400};
146
+ --ring-color: rgba(99, 102, 241, 0.45);
147
+
148
+ --shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
149
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4),
150
+ 0 1px 3px 0 rgba(0, 0, 0, 0.3);
151
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
152
+ --shadow-lg: 0 16px 32px -8px rgba(0, 0, 0, 0.6);
153
+
154
+ --hairline-color: #{$zinc-800};
155
+ --hairline-strong: #{$zinc-700};
156
+ }
157
+ }
158
+
159
+ .cny-dark {
160
+ --bg-base: #{$zinc-950};
161
+ --bg-surface-low: #{$zinc-900};
162
+ --bg-surface-lowest: #{$zinc-800};
163
+ --bg-surface-edge: #{$zinc-700};
164
+
165
+ --text-primary: #{$zinc-50};
166
+ --text-secondary: #{$zinc-300};
167
+ --text-muted: #{$zinc-400};
168
+ --text-disabled: #{$zinc-600};
169
+ --text-accent: #{$indigo-400};
170
+
171
+ --color-primary: #{$indigo-500};
172
+ --color-primary-hover: #{$indigo-400};
173
+ --color-primary-active:#{$indigo-600};
174
+ --color-primary-soft: rgba(99, 102, 241, 0.15);
175
+
176
+ --color-neutral: #{$zinc-50};
177
+ --color-neutral-hover: #ffffff;
178
+ --color-neutral-soft: #{$zinc-800};
179
+ --color-neutral-on: #{$zinc-950};
180
+
181
+ --color-danger-soft: rgba(220, 38, 38, 0.16);
182
+ --color-warning-soft: rgba(217, 119, 6, 0.16);
183
+ --color-success-soft: rgba(5, 150, 105, 0.16);
184
+ --color-info-soft: rgba(2, 132, 199, 0.16);
185
+
186
+ --border-color: #{$zinc-800};
187
+ --border-color-strong: #{$zinc-700};
188
+ --border-focus: #{$indigo-400};
189
+ --ring-color: rgba(99, 102, 241, 0.45);
190
+
191
+ --shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
192
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4),
193
+ 0 1px 3px 0 rgba(0, 0, 0, 0.3);
194
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
195
+ --shadow-lg: 0 16px 32px -8px rgba(0, 0, 0, 0.6);
196
+
197
+ --hairline-color: #{$zinc-800};
198
+ --hairline-strong: #{$zinc-700};
199
+ }