kedhar-ui 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.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # kedhar-ui
2
+
3
+ Shared design system for Kedhar's projects. CSS design tokens, base styles, component styles, per-site theme files, and reusable Astro components.
4
+
5
+ ```
6
+ npm install @kedharsairam/kedhar-ui
7
+ ```
8
+
9
+ ## What's included
10
+
11
+ - **Design tokens** — spacing scale, border radius, typography, shadows, transitions, z-index
12
+ - **Base reset** — box-sizing, body defaults, link/button/image styling, focus-visible, selection
13
+ - **Component styles** — CSS-only ripple effect, scroll-to-top button, theme toggle, card, badge, section container
14
+ - **Per-site themes** — Indigo (portfolio), Amber (projects), Emerald (blog), Slate (mssql-scripts)
15
+ - **Astro components** — ThemeToggle, ScrollToTop (import from `src/components/`)
16
+
17
+ ## Usage in Astro sites
18
+
19
+ ```astro
20
+ ---
21
+ // Layout frontmatter
22
+ import '@kedharsairam/kedhar-ui/src/styles/tokens.css';
23
+ import '@kedharsairam/kedhar-ui/src/styles/base.css';
24
+ import '@kedharsairam/kedhar-ui/src/styles/components.css';
25
+ import '@kedharsairam/kedhar-ui/src/styles/themes/indigo.css';
26
+
27
+ import ThemeToggle from '@kedharsairam/kedhar-ui/src/components/ThemeToggle.astro';
28
+ import ScrollToTop from '@kedharsairam/kedhar-ui/src/components/ScrollToTop.astro';
29
+ ---
30
+
31
+ <ThemeToggle />
32
+ <ScrollToTop />
33
+ ```
34
+
35
+ ## Usage in plain HTML
36
+
37
+ ```html
38
+ <link rel="stylesheet" href="https://unpkg.com/@kedharsairam/kedhar-ui/dist/kedhar-ui-slate.css">
39
+ ```
40
+
41
+ ## Development
42
+
43
+ ```bash
44
+ npm install
45
+ npm run build # produces dist/ bundles
46
+ npm publish # publishes to npm (auto-runs build)
47
+ ```
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,487 @@
1
+ /* kedhar-ui v0.1.0 — https://github.com/kedharsairam/kedhar-ui */
2
+
3
+ /* ══════════════════════════════════════════════════════════════
4
+ kedhar-ui — Design Tokens
5
+ ══════════════════════════════════════════════════════════════
6
+ These tokens are consumed by base.css and components.css.
7
+ Each site imports its theme file to set the --ks-color-* values.
8
+ ══════════════════════════════════════════════════════════════ */
9
+
10
+ :root {
11
+ /* ─── Spacing Scale ──────────────────────────────────────── */
12
+ --ks-space-1: 4px;
13
+ --ks-space-2: 8px;
14
+ --ks-space-3: 12px;
15
+ --ks-space-4: 16px;
16
+ --ks-space-5: 20px;
17
+ --ks-space-6: 24px;
18
+ --ks-space-8: 32px;
19
+ --ks-space-10: 40px;
20
+ --ks-space-12: 48px;
21
+ --ks-space-16: 64px;
22
+ --ks-space-20: 80px;
23
+
24
+ /* ─── Border Radius ──────────────────────────────────────── */
25
+ --ks-radius-sm: 6px;
26
+ --ks-radius-md: 10px;
27
+ --ks-radius-lg: 16px;
28
+ --ks-radius-xl: 24px;
29
+ --ks-radius-full: 9999px;
30
+
31
+ /* ─── Typography ─────────────────────────────────────────── */
32
+ --ks-font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
33
+ --ks-font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
34
+
35
+ --ks-text-xs: 0.75rem;
36
+ --ks-text-sm: 0.875rem;
37
+ --ks-text-base: 1rem;
38
+ --ks-text-lg: 1.125rem;
39
+ --ks-text-xl: 1.25rem;
40
+ --ks-text-2xl: 1.5rem;
41
+ --ks-text-3xl: 1.875rem;
42
+ --ks-text-4xl: 2.25rem;
43
+
44
+ --ks-leading-tight: 1.25;
45
+ --ks-leading-normal: 1.5;
46
+ --ks-leading-relaxed: 1.625;
47
+
48
+ --ks-weight-normal: 400;
49
+ --ks-weight-medium: 500;
50
+ --ks-weight-semibold: 600;
51
+ --ks-weight-bold: 700;
52
+
53
+ /* ─── Shadows ────────────────────────────────────────────── */
54
+ --ks-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
55
+ --ks-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
56
+ --ks-shadow-lg: 0 10px 25px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.04);
57
+ --ks-shadow-xl: 0 20px 50px -8px rgba(0, 0, 0, 0.1);
58
+
59
+ /* ─── Transitions ────────────────────────────────────────── */
60
+ --ks-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
61
+ --ks-ease-in: cubic-bezier(0.4, 0, 0.68, 0.06);
62
+ --ks-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
63
+ --ks-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
64
+
65
+ --ks-duration-fast: 150ms;
66
+ --ks-duration-normal: 250ms;
67
+ --ks-duration-slow: 400ms;
68
+
69
+ /* ─── Z-Index Scale ──────────────────────────────────────── */
70
+ --ks-z-base: 1;
71
+ --ks-z-dropdown: 10;
72
+ --ks-z-sticky: 20;
73
+ --ks-z-modal: 30;
74
+ --ks-z-toast: 40;
75
+ --ks-z-tooltip: 50;
76
+
77
+ /* ─── Layout ─────────────────────────────────────────────── */
78
+ --ks-max-width: 1200px;
79
+ }
80
+
81
+ /* ─── Dark mode token overrides ────────────────────────────── */
82
+ .dark {
83
+ --ks-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
84
+ --ks-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -2px rgba(0, 0, 0, 0.2);
85
+ --ks-shadow-lg: 0 10px 25px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -4px rgba(0, 0, 0, 0.2);
86
+ --ks-shadow-xl: 0 20px 50px -8px rgba(0, 0, 0, 0.3);
87
+ }
88
+
89
+ /* ══════════════════════════════════════════════════════════════
90
+ kedhar-ui — Base Styles
91
+ ══════════════════════════════════════════════════════════════
92
+ Reset + base element styling. Import tokens.css first.
93
+ ══════════════════════════════════════════════════════════════ */
94
+
95
+ /* ─── Box-Sizing Reset ─────────────────────────────────────── */
96
+ *,
97
+ *::before,
98
+ *::after {
99
+ box-sizing: border-box;
100
+ }
101
+
102
+ /* ─── Body Defaults ────────────────────────────────────────── */
103
+ html {
104
+ scroll-behavior: smooth;
105
+ -webkit-text-size-adjust: 100%;
106
+ }
107
+
108
+ body {
109
+ margin: 0;
110
+ padding: 0;
111
+ font-family: var(--ks-font-sans);
112
+ font-size: var(--ks-text-base);
113
+ line-height: var(--ks-leading-normal);
114
+ color: var(--ks-color-text);
115
+ background-color: var(--ks-color-bg);
116
+ -webkit-font-smoothing: antialiased;
117
+ -moz-osx-font-smoothing: grayscale;
118
+ transition:
119
+ background-color var(--ks-duration-normal) var(--ks-ease-out),
120
+ color var(--ks-duration-normal) var(--ks-ease-out);
121
+ }
122
+
123
+ /* ─── Links ────────────────────────────────────────────────── */
124
+ a {
125
+ color: var(--ks-color-primary);
126
+ text-decoration: none;
127
+ transition: color var(--ks-duration-fast) var(--ks-ease-out);
128
+ }
129
+
130
+ a:hover {
131
+ color: var(--ks-color-primary-hover);
132
+ }
133
+
134
+ /* ─── Images ───────────────────────────────────────────────── */
135
+ img {
136
+ max-width: 100%;
137
+ height: auto;
138
+ display: block;
139
+ }
140
+
141
+ /* ─── Buttons ──────────────────────────────────────────────── */
142
+ button {
143
+ font-family: inherit;
144
+ font-size: inherit;
145
+ cursor: pointer;
146
+ }
147
+
148
+ /* ─── Code ─────────────────────────────────────────────────── */
149
+ code,
150
+ pre {
151
+ font-family: var(--ks-font-mono);
152
+ }
153
+
154
+ /* ─── Focus Visible ────────────────────────────────────────── */
155
+ :focus-visible {
156
+ outline: 2px solid var(--ks-color-primary);
157
+ outline-offset: 2px;
158
+ border-radius: var(--ks-radius-sm);
159
+ }
160
+
161
+ /* ─── Selection ────────────────────────────────────────────── */
162
+ ::selection {
163
+ background-color: var(--ks-color-primary);
164
+ color: #ffffff;
165
+ }
166
+
167
+ /* ══════════════════════════════════════════════════════════════
168
+ kedhar-ui — Component Styles
169
+ ══════════════════════════════════════════════════════════════
170
+ Ripple, scroll-to-top, theme toggle, and other shared components.
171
+ Import tokens.css and base.css first.
172
+ ══════════════════════════════════════════════════════════════ */
173
+
174
+ /* ══════════════════════════════════════════════════════════════
175
+ 1. CSS Ripple Effect
176
+ ══════════════════════════════════════════════════════════════
177
+ Pure CSS — no JavaScript required.
178
+ Expands a circle from center on :active.
179
+ ══════════════════════════════════════════════════════════════ */
180
+ .ks-ripple {
181
+ position: relative;
182
+ overflow: hidden;
183
+ cursor: pointer;
184
+ -webkit-tap-highlight-color: transparent;
185
+ }
186
+
187
+ .ks-ripple::after {
188
+ content: '';
189
+ position: absolute;
190
+ top: 50%;
191
+ left: 50%;
192
+ width: 0;
193
+ height: 0;
194
+ border-radius: 50%;
195
+ background: currentColor;
196
+ opacity: 0.15;
197
+ transform: translate(-50%, -50%);
198
+ pointer-events: none;
199
+ will-change: width, height, opacity;
200
+ }
201
+
202
+ .ks-ripple:active::after {
203
+ width: 300%;
204
+ height: 300%;
205
+ opacity: 0;
206
+ transition:
207
+ width 0.4s var(--ks-ease-out),
208
+ height 0.4s var(--ks-ease-out),
209
+ opacity 0.4s var(--ks-ease-out);
210
+ }
211
+
212
+
213
+ /* ══════════════════════════════════════════════════════════════
214
+ 2. Scroll-to-Top Button
215
+ ══════════════════════════════════════════════════════════════
216
+ Requires JS to toggle .visible class.
217
+ ══════════════════════════════════════════════════════════════ */
218
+ .ks-scroll-top {
219
+ position: fixed;
220
+ bottom: var(--ks-space-8);
221
+ right: var(--ks-space-8);
222
+ width: 44px;
223
+ height: 44px;
224
+ border-radius: var(--ks-radius-full);
225
+ border: none;
226
+ display: flex;
227
+ align-items: center;
228
+ justify-content: center;
229
+ cursor: pointer;
230
+ opacity: 0;
231
+ visibility: hidden;
232
+ transform: translateY(12px);
233
+ pointer-events: none;
234
+ z-index: var(--ks-z-sticky);
235
+ box-shadow: var(--ks-shadow-lg);
236
+ background: var(--ks-color-surface);
237
+ color: var(--ks-color-text-muted);
238
+ transition:
239
+ opacity var(--ks-duration-normal) var(--ks-ease-out),
240
+ visibility var(--ks-duration-normal) var(--ks-ease-out),
241
+ transform var(--ks-duration-normal) var(--ks-ease-out),
242
+ background var(--ks-duration-normal) var(--ks-ease-out),
243
+ color var(--ks-duration-normal) var(--ks-ease-out);
244
+ overflow: hidden;
245
+ }
246
+
247
+ .ks-scroll-top.visible {
248
+ opacity: 1;
249
+ visibility: visible;
250
+ transform: translateY(0);
251
+ pointer-events: auto;
252
+ }
253
+
254
+ .ks-scroll-top:hover {
255
+ background: var(--ks-color-surface-hover);
256
+ color: var(--ks-color-primary);
257
+ box-shadow: var(--ks-shadow-lg), 0 0 0 2px var(--ks-color-primary-alpha);
258
+ }
259
+
260
+ .ks-scroll-top svg {
261
+ width: 20px;
262
+ height: 20px;
263
+ stroke: currentColor;
264
+ fill: none;
265
+ stroke-width: 2;
266
+ stroke-linecap: round;
267
+ stroke-linejoin: round;
268
+ }
269
+
270
+
271
+ /* ══════════════════════════════════════════════════════════════
272
+ 3. Theme Toggle (Light / Dark)
273
+ ══════════════════════════════════════════════════════════════
274
+ Animated pill switch with sun/moon icons on the knob.
275
+ ══════════════════════════════════════════════════════════════ */
276
+ .ks-theme-toggle {
277
+ position: relative;
278
+ width: 56px;
279
+ height: 30px;
280
+ border-radius: var(--ks-radius-full);
281
+ border: 2px solid var(--ks-color-border);
282
+ background: var(--ks-color-surface);
283
+ cursor: pointer;
284
+ display: flex;
285
+ align-items: center;
286
+ padding: 0;
287
+ flex-shrink: 0;
288
+ transition:
289
+ background var(--ks-duration-normal) var(--ks-ease-spring),
290
+ border-color var(--ks-duration-normal) var(--ks-ease-out);
291
+ overflow: hidden;
292
+ -webkit-tap-highlight-color: transparent;
293
+ }
294
+
295
+ .ks-theme-toggle:hover {
296
+ border-color: var(--ks-color-primary);
297
+ }
298
+
299
+ /* Knob */
300
+ .ks-toggle-knob {
301
+ position: absolute;
302
+ top: 2px;
303
+ left: 2px;
304
+ width: 22px;
305
+ height: 22px;
306
+ border-radius: var(--ks-radius-full);
307
+ display: flex;
308
+ align-items: center;
309
+ justify-content: center;
310
+ transition:
311
+ transform var(--ks-duration-normal) var(--ks-ease-spring),
312
+ background var(--ks-duration-normal) var(--ks-ease-out);
313
+ z-index: 2;
314
+ background: var(--ks-color-primary);
315
+ color: #ffffff;
316
+ box-shadow: var(--ks-shadow-sm);
317
+ }
318
+
319
+ /* Dark position */
320
+ .dark .ks-toggle-knob {
321
+ transform: translateX(26px);
322
+ }
323
+
324
+ /* Icons inside knob */
325
+ .ks-toggle-knob svg {
326
+ width: 13px;
327
+ height: 13px;
328
+ fill: none;
329
+ stroke: currentColor;
330
+ stroke-width: 2;
331
+ stroke-linecap: round;
332
+ stroke-linejoin: round;
333
+ transition: transform var(--ks-duration-normal) var(--ks-ease-spring);
334
+ }
335
+
336
+ .dark .ks-toggle-knob svg {
337
+ transform: rotate(90deg);
338
+ }
339
+
340
+ /* Track background halves */
341
+ .ks-toggle-track {
342
+ display: flex;
343
+ width: 100%;
344
+ height: 100%;
345
+ }
346
+
347
+ .ks-toggle-track span {
348
+ flex: 1;
349
+ display: flex;
350
+ align-items: center;
351
+ justify-content: center;
352
+ font-size: 12px;
353
+ color: var(--ks-color-text-muted);
354
+ transition: color var(--ks-duration-fast) var(--ks-ease-out);
355
+ }
356
+
357
+ .dark .ks-toggle-track span:first-child {
358
+ color: transparent;
359
+ }
360
+
361
+ .dark .ks-toggle-track span:last-child {
362
+ color: var(--ks-color-text-muted);
363
+ }
364
+
365
+ .ks-toggle-track span:first-child {
366
+ color: var(--ks-color-text-muted);
367
+ }
368
+
369
+ .ks-toggle-track span:last-child {
370
+ color: transparent;
371
+ }
372
+
373
+
374
+ /* ══════════════════════════════════════════════════════════════
375
+ 4. Card
376
+ ══════════════════════════════════════════════════════════════
377
+ Reusable card component with consistent styling.
378
+ ══════════════════════════════════════════════════════════════ */
379
+ .ks-card {
380
+ background: var(--ks-color-surface);
381
+ border: 1px solid var(--ks-color-border);
382
+ border-radius: var(--ks-radius-lg);
383
+ padding: var(--ks-space-6);
384
+ transition:
385
+ box-shadow var(--ks-duration-normal) var(--ks-ease-out),
386
+ border-color var(--ks-duration-normal) var(--ks-ease-out),
387
+ transform var(--ks-duration-normal) var(--ks-ease-out);
388
+ }
389
+
390
+ .ks-card:hover {
391
+ border-color: var(--ks-color-primary-alpha);
392
+ box-shadow: var(--ks-shadow-md);
393
+ transform: translateY(-2px);
394
+ }
395
+
396
+
397
+ /* ══════════════════════════════════════════════════════════════
398
+ 5. Badge / Tag
399
+ ══════════════════════════════════════════════════════════════
400
+ Small label for status, tags, metadata.
401
+ ══════════════════════════════════════════════════════════════ */
402
+ .ks-badge {
403
+ display: inline-flex;
404
+ align-items: center;
405
+ padding: 2px var(--ks-space-2);
406
+ border-radius: var(--ks-radius-sm);
407
+ font-size: var(--ks-text-xs);
408
+ font-weight: var(--ks-weight-medium);
409
+ line-height: 1.4;
410
+ background: var(--ks-color-surface-hover);
411
+ color: var(--ks-color-text-muted);
412
+ border: 1px solid var(--ks-color-border);
413
+ }
414
+
415
+
416
+ /* ══════════════════════════════════════════════════════════════
417
+ 6. Section Container
418
+ ══════════════════════════════════════════════════════════════ */
419
+ .ks-section {
420
+ padding: var(--ks-space-16) var(--ks-space-4);
421
+ max-width: var(--ks-max-width);
422
+ margin: 0 auto;
423
+ }
424
+
425
+ @media (min-width: 640px) {
426
+ .ks-section {
427
+ padding: var(--ks-space-20) var(--ks-space-6);
428
+ }
429
+ }
430
+
431
+ @media (min-width: 1024px) {
432
+ .ks-section {
433
+ padding: var(--ks-space-20) var(--ks-space-8);
434
+ }
435
+ }
436
+
437
+
438
+ /* ══════════════════════════════════════════════════════════════
439
+ 7. Utility Classes
440
+ ══════════════════════════════════════════════════════════════ */
441
+ .ks-sr-only {
442
+ position: absolute;
443
+ width: 1px;
444
+ height: 1px;
445
+ padding: 0;
446
+ margin: -1px;
447
+ overflow: hidden;
448
+ clip: rect(0, 0, 0, 0);
449
+ white-space: nowrap;
450
+ border: 0;
451
+ }
452
+
453
+ /* ══════════════════════════════════════════════════════════════
454
+ Theme: Amber — Projects site
455
+ ══════════════════════════════════════════════════════════════ */
456
+
457
+ :root {
458
+ --ks-color-primary: #d97706;
459
+ --ks-color-primary-hover: #b45309;
460
+ --ks-color-primary-light: #fef3c7;
461
+ --ks-color-primary-dark: #92400e;
462
+ --ks-color-primary-alpha: rgba(217, 119, 6, 0.15);
463
+
464
+ --ks-color-bg: #faf8f5;
465
+ --ks-color-surface: #ffffff;
466
+ --ks-color-surface-hover: #f5f2ed;
467
+ --ks-color-border: #e8e2d8;
468
+ --ks-color-text: #1c1917;
469
+ --ks-color-text-muted: #78716c;
470
+ --ks-color-text-inverse: #ffffff;
471
+ }
472
+
473
+ .dark {
474
+ --ks-color-primary: #f59e0b;
475
+ --ks-color-primary-hover: #d97706;
476
+ --ks-color-primary-light: #292524;
477
+ --ks-color-primary-dark: #fbbf24;
478
+ --ks-color-primary-alpha: rgba(245, 158, 11, 0.15);
479
+
480
+ --ks-color-bg: #0f0d0a;
481
+ --ks-color-surface: #1c1917;
482
+ --ks-color-surface-hover: #292524;
483
+ --ks-color-border: #3f3a36;
484
+ --ks-color-text: #f5f0eb;
485
+ --ks-color-text-muted: #a8a29e;
486
+ --ks-color-text-inverse: #0f0d0a;
487
+ }