unigrid.css 0.3.9 → 0.3.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unigrid.css",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "A CSS grid framework inspired by Massimo Vignelli's Unigrid system for the National Park Service. Vertical rhythm, square modules, BEM naming.",
5
5
  "main": "dist/unigrid.css",
6
6
  "style": "dist/unigrid.css",
@@ -0,0 +1,905 @@
1
+ // ==========================================================================
2
+ // Unigrid Custom Components for Bootstrap
3
+ //
4
+ // Additions and overrides not covered by Bootstrap's variable system.
5
+ // Every design token — colors, type scale, spacing, rhythm, breakpoints —
6
+ // comes from helpers.scss, which re-exports src/scss/_variables.scss and
7
+ // src/scss/_mixins.scss so the theme shares the single source of truth
8
+ // with the Unigrid framework itself.
9
+ // ==========================================================================
10
+
11
+ @use "helpers" as *;
12
+
13
+ // ---- Vertical rhythm token ----
14
+ // Mirrors src/scss/_reset.scss: $ug-leading px on mobile, $ug-leading-desktop
15
+ // px on desktop (≥ $ug-prose-max-width). Every .ug-* / .ug-rhythm-* rule in
16
+ // this file relies on var(--ug-leading) so the rhythm adapts at runtime.
17
+ :root {
18
+ --ug-leading: #{$ug-leading}px;
19
+ }
20
+ @media screen and (min-width: $ug-prose-max-width) {
21
+ :root {
22
+ --ug-leading: #{$ug-leading-desktop}px;
23
+ }
24
+ }
25
+
26
+ // ---- Variable font support ----
27
+ @supports (font-variation-settings: normal) {
28
+ :root {
29
+ font-family: $ug-font-family-variable;
30
+ }
31
+ }
32
+
33
+ :root {
34
+ font-feature-settings: $ug-font-feature-settings;
35
+ }
36
+
37
+ // ==========================================================================
38
+ // Unigrid Header Band — <header class="ug-header">
39
+ // 1:1 port of src/scss/_header.scss
40
+ // ==========================================================================
41
+ .ug-header {
42
+ display: flex;
43
+ align-items: flex-start;
44
+ gap: var(--ug-leading);
45
+ background-color: $ug-black;
46
+ color: $ug-white;
47
+ padding: var(--ug-leading) calc(var(--ug-leading) * 1.5);
48
+ width: 100%;
49
+ flex-wrap: wrap;
50
+
51
+ &__logo {
52
+ height: calc(var(--ug-leading) * 3);
53
+ flex-shrink: 0;
54
+
55
+ img, svg { height: 100%; width: auto; }
56
+ }
57
+
58
+ &__title {
59
+ display: flex;
60
+ flex-direction: column;
61
+ gap: 0;
62
+ flex-shrink: 0;
63
+
64
+ h1, a {
65
+ @include ug-font-size("3xl");
66
+ @include ug-font-weight("bold");
67
+ @include ug-rhythm-line-height(2);
68
+ letter-spacing: -0.02em;
69
+ text-transform: none;
70
+ margin: 0;
71
+ color: $ug-white;
72
+ }
73
+
74
+ a { text-decoration: none; }
75
+ }
76
+
77
+ &__name {
78
+ @include ug-font-size("3xl");
79
+ @include ug-font-weight("bold");
80
+ @include ug-rhythm-line-height(2);
81
+ letter-spacing: -0.02em;
82
+ margin: 0;
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ &__subtitle {
87
+ @include ug-font-size("lg");
88
+ @include ug-font-weight("light");
89
+ @include ug-rhythm-line-height(1);
90
+ opacity: 0.85;
91
+ margin: 0;
92
+ }
93
+
94
+ // Embed Bootstrap navbar inside header
95
+ .navbar {
96
+ flex: 1;
97
+ padding: 0;
98
+
99
+ .navbar-nav .nav-link {
100
+ color: rgba(255, 255, 255, 0.7);
101
+ &:hover { color: $ug-white; background-color: rgba(255, 255, 255, 0.1); }
102
+ &.active { color: $ug-white; }
103
+ }
104
+
105
+ .navbar-toggler {
106
+ color: $ug-white;
107
+ border-color: rgba(255, 255, 255, 0.5);
108
+ }
109
+ }
110
+
111
+ // ---- Modifiers ----
112
+ &--compact {
113
+ padding: calc(var(--ug-leading) * 0.5) calc(var(--ug-leading) * 1.5);
114
+
115
+ .ug-header__logo {
116
+ width: auto;
117
+ height: calc(var(--ug-leading) * 2);
118
+ }
119
+
120
+ .ug-header__title h1,
121
+ .ug-header__title a,
122
+ .ug-header__name {
123
+ @include ug-font-size("2xl");
124
+ @include ug-rhythm-line-height(1);
125
+ }
126
+
127
+ .ug-header__subtitle {
128
+ @include ug-font-size("sm");
129
+ @include ug-rhythm-line-height(1);
130
+ }
131
+ }
132
+
133
+ &--brown { background-color: $ug-brown; }
134
+
135
+ &--full { grid-column: 1 / -1; }
136
+
137
+ // ---- Responsive ----
138
+ @include ug-breakpoint-down("md") {
139
+ &__title h1,
140
+ &__title a,
141
+ &__name {
142
+ @include ug-font-size("2xl");
143
+ }
144
+
145
+ &__subtitle {
146
+ @include ug-font-size("base");
147
+ }
148
+
149
+ &__logo {
150
+ width: 2.5rem;
151
+ }
152
+ }
153
+
154
+ @include ug-breakpoint-down("sm") {
155
+ &__title h1,
156
+ &__title a,
157
+ &__name {
158
+ @include ug-font-size("lg");
159
+ }
160
+
161
+ &__subtitle {
162
+ @include ug-font-size("xs");
163
+ }
164
+ }
165
+ }
166
+
167
+ // ==========================================================================
168
+ // Unigrid Section Title — .ug-section
169
+ // 1:1 port of src/scss/_section.scss
170
+ // ==========================================================================
171
+ .ug-section {
172
+ width: 100%;
173
+ background-color: $ug-black;
174
+ color: $ug-white;
175
+ padding: var(--ug-leading) calc(var(--ug-leading) * 1.5);
176
+
177
+ &__title {
178
+ @include ug-rhythm-font-size(1.6875);
179
+ @include ug-rhythm-line-height(1.5);
180
+ @include ug-font-weight("black");
181
+ letter-spacing: -0.01em;
182
+ margin: 0;
183
+ }
184
+
185
+ &__subtitle {
186
+ @include ug-font-size("sm");
187
+ @include ug-font-weight("light");
188
+ @include ug-rhythm-line-height(1);
189
+ opacity: 0.7;
190
+ margin: 0;
191
+ }
192
+
193
+ // ---- Size modifiers ----
194
+ &--compact {
195
+ padding: calc(var(--ug-leading) * 0.5) calc(var(--ug-leading) * 1.5);
196
+
197
+ .ug-section__title {
198
+ @include ug-rhythm-font-size(1.2);
199
+ @include ug-rhythm-line-height(1);
200
+ }
201
+ }
202
+
203
+ &--large {
204
+ padding: calc(var(--ug-leading) * 2) calc(var(--ug-leading) * 1.5);
205
+
206
+ .ug-section__title {
207
+ @include ug-rhythm-font-size(2.5);
208
+ @include ug-rhythm-line-height(2);
209
+ }
210
+ }
211
+
212
+ // ---- Color modifiers ----
213
+ &--brown { background-color: $ug-brown; }
214
+ &--red { background-color: $ug-red; }
215
+ &--green { background-color: $ug-green; }
216
+ &--blue { background-color: $ug-blue; }
217
+
218
+ &--light {
219
+ background-color: $ug-warm-gray;
220
+ color: $ug-black;
221
+ }
222
+
223
+ // ---- Responsive ----
224
+ @include ug-breakpoint-down("md") {
225
+ padding: var(--ug-leading) var(--ug-leading);
226
+
227
+ &--large {
228
+ padding: calc(var(--ug-leading) * 1.5) var(--ug-leading);
229
+ }
230
+
231
+ &--compact {
232
+ padding: calc(var(--ug-leading) * 0.5) var(--ug-leading);
233
+ }
234
+ }
235
+
236
+ @include ug-breakpoint-down("sm") {
237
+ .ug-section__title {
238
+ @include ug-rhythm-font-size(1.2);
239
+ }
240
+ }
241
+ }
242
+
243
+ // ==========================================================================
244
+ // Unigrid Label Divider
245
+ // 1:1 port of src/scss/_components.scss .ug-label-divider
246
+ // ==========================================================================
247
+ .ug-label-divider {
248
+ @include ug-font-size("sm");
249
+ @include ug-font-weight("bold");
250
+ line-height: calc(var(--ug-leading) - 2px);
251
+ margin-bottom: calc(var(--ug-leading) * 0.5 - 2px);
252
+ text-transform: uppercase;
253
+ letter-spacing: 0.1em;
254
+ color: $ug-medium-gray;
255
+ border-bottom: 2px solid $ug-black;
256
+ @include ug-rhythm-margin-top(0.5);
257
+
258
+ &--bold {
259
+ line-height: calc(var(--ug-leading) - 3px);
260
+ border-bottom-width: 3px;
261
+ margin-bottom: calc(var(--ug-leading) * 0.5 - 3px);
262
+ }
263
+
264
+ &--light {
265
+ border-bottom-color: $ug-light-gray;
266
+ }
267
+ }
268
+
269
+ // ==========================================================================
270
+ // Unigrid Infobox
271
+ // 1:1 port of src/scss/_components.scss .ug-infobox
272
+ // ==========================================================================
273
+ .ug-infobox {
274
+ background-color: $ug-warm-gray;
275
+ @include ug-rhythm-padding(1, 1);
276
+ border-left: 3px solid $ug-black;
277
+ border-radius: $ug-border-radius;
278
+
279
+ &--dark {
280
+ background-color: $ug-black;
281
+ color: $ug-white;
282
+ border-left-color: $ug-white;
283
+ }
284
+
285
+ &__title {
286
+ @include ug-font-size("sm");
287
+ @include ug-font-weight("bold");
288
+ @include ug-rhythm-line-height(1);
289
+ text-transform: uppercase;
290
+ letter-spacing: 0.08em;
291
+ margin-bottom: 0;
292
+ }
293
+ }
294
+
295
+ // ==========================================================================
296
+ // Unigrid Divider — 1:1 port of src/scss/_components.scss .ug-divider
297
+ // ==========================================================================
298
+ .ug-divider {
299
+ border: none;
300
+ border-top: 1px solid $ug-light-gray;
301
+ padding-top: calc(var(--ug-leading) * 0.5 - 1px);
302
+ padding-bottom: calc(var(--ug-leading) * 0.5);
303
+
304
+ &--bold {
305
+ border-top: 3px solid $ug-black;
306
+ padding-top: calc(var(--ug-leading) * 0.5 - 3px);
307
+ }
308
+ }
309
+
310
+ // ==========================================================================
311
+ // Forms: Unigrid flat style (matches .ug-form)
312
+ // Bootstrap variables already handle sharp corners, light-gray borders,
313
+ // black focus, and no focus shadow via the `with (...)` block.
314
+ // ==========================================================================
315
+ .form-label {
316
+ display: block;
317
+ @include ug-font-size("sm");
318
+ @include ug-font-weight("bold");
319
+ @include ug-rhythm-line-height(1);
320
+ @include ug-rhythm-margin-top(0.5);
321
+ color: $ug-black;
322
+ margin-bottom: 0;
323
+ }
324
+
325
+ .col-form-label {
326
+ @include ug-font-weight("bold");
327
+ color: $ug-black;
328
+ }
329
+
330
+ .form-control,
331
+ .form-select,
332
+ .form-control-plaintext {
333
+ transition: border-color 0.15s;
334
+ box-shadow: none;
335
+
336
+ &::placeholder { color: $ug-medium-gray; }
337
+
338
+ &:focus {
339
+ box-shadow: none;
340
+ border-color: $ug-black;
341
+ }
342
+ }
343
+
344
+ // Custom chevron matching src/scss/_forms.scss
345
+ .form-select {
346
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23666' stroke-width='2' fill='none'/%3E%3C/svg%3E");
347
+ background-repeat: no-repeat;
348
+ background-position: right calc(var(--ug-leading) * 0.5) center;
349
+ background-size: 12px;
350
+ }
351
+
352
+ .form-text {
353
+ @include ug-font-size("xs");
354
+ @include ug-rhythm-line-height(1);
355
+ color: $ug-medium-gray;
356
+ }
357
+
358
+ // Checkboxes: sharp corners, black accent. Radios stay circular.
359
+ .form-check-input {
360
+ border-color: $ug-light-gray;
361
+ box-shadow: none;
362
+ accent-color: $ug-black;
363
+ transition: border-color 0.15s, background-color 0.15s;
364
+
365
+ &[type="checkbox"] { border-radius: 0; }
366
+
367
+ &:focus {
368
+ border-color: $ug-black;
369
+ box-shadow: none;
370
+ }
371
+
372
+ &:checked {
373
+ background-color: $ug-black;
374
+ border-color: $ug-black;
375
+ }
376
+
377
+ &:disabled { opacity: 0.5; }
378
+ }
379
+
380
+ .form-check-label { color: $ug-black; }
381
+
382
+ // Disabled state
383
+ .form-control:disabled,
384
+ .form-select:disabled,
385
+ .form-control[readonly] {
386
+ background-color: $ug-warm-gray;
387
+ opacity: 0.7;
388
+ }
389
+
390
+ // Invalid state: red border, no Bootstrap overlay icon
391
+ .form-control.is-invalid,
392
+ .was-validated .form-control:invalid,
393
+ .form-select.is-invalid,
394
+ .was-validated .form-select:invalid {
395
+ border-color: $ug-red;
396
+ background-image: none;
397
+ padding-right: leading(0.5);
398
+
399
+ &:focus {
400
+ border-color: $ug-red;
401
+ box-shadow: none;
402
+ }
403
+ }
404
+
405
+ .invalid-feedback {
406
+ @include ug-font-size("xs");
407
+ @include ug-rhythm-line-height(1);
408
+ color: $ug-red;
409
+ }
410
+
411
+ // Input group addon
412
+ .input-group-text {
413
+ background-color: $ug-warm-gray;
414
+ border-color: $ug-light-gray;
415
+ color: $ug-black;
416
+ @include ug-font-weight("bold");
417
+ @include ug-font-size("sm");
418
+ }
419
+
420
+ // ==========================================================================
421
+ // Nav tabs: Unigrid underline style (matches .ug-tabs)
422
+ // ==========================================================================
423
+ .nav-tabs {
424
+ --bs-nav-tabs-border-width: 2px;
425
+ --bs-nav-tabs-border-color: #{$ug-light-gray};
426
+ border-bottom-width: 2px;
427
+
428
+ .nav-link {
429
+ color: $ug-medium-gray;
430
+ @include ug-font-size("sm");
431
+ @include ug-font-weight("bold");
432
+ @include ug-rhythm-line-height(1);
433
+ padding: calc(var(--ug-leading) * 0.5) var(--ug-leading);
434
+ border: none;
435
+ border-bottom: 2px solid transparent;
436
+ border-radius: 0;
437
+ margin-bottom: -2px;
438
+ background: none;
439
+ transition: color 0.15s, border-color 0.15s;
440
+
441
+ &:hover,
442
+ &:focus {
443
+ color: $ug-black;
444
+ border-color: transparent;
445
+ border-bottom-color: transparent;
446
+ isolation: auto;
447
+ background: none;
448
+ }
449
+
450
+ &.active {
451
+ color: $ug-black;
452
+ background-color: transparent;
453
+ border-color: transparent;
454
+ border-bottom-color: $ug-black;
455
+ }
456
+
457
+ &.disabled { color: $ug-light-gray; }
458
+ }
459
+
460
+ .nav-item.show .nav-link {
461
+ color: $ug-black;
462
+ background-color: transparent;
463
+ border-color: transparent transparent $ug-black;
464
+ }
465
+ }
466
+
467
+ // ==========================================================================
468
+ // Nav pills: Unigrid flat (sharp corners, black active)
469
+ // ==========================================================================
470
+ .nav-pills {
471
+ --bs-nav-pills-border-radius: 0;
472
+ gap: calc(var(--ug-leading) * 0.25);
473
+
474
+ .nav-link {
475
+ color: $ug-medium-gray;
476
+ @include ug-font-size("sm");
477
+ @include ug-font-weight("bold");
478
+ @include ug-rhythm-line-height(1);
479
+ padding: calc(var(--ug-leading) * 0.5) var(--ug-leading);
480
+ border-radius: 0;
481
+ background: none;
482
+ transition: color 0.15s, background-color 0.15s;
483
+
484
+ &:hover {
485
+ color: $ug-black;
486
+ background-color: $ug-warm-gray;
487
+ }
488
+
489
+ &.active,
490
+ &.show {
491
+ color: $ug-white;
492
+ background-color: $ug-black;
493
+
494
+ &:hover { background-color: $ug-dark-gray; }
495
+ }
496
+ }
497
+ }
498
+
499
+ // ==========================================================================
500
+ // Pagination: flat Unigrid style (matches .ug-pagination)
501
+ // ==========================================================================
502
+ .pagination { gap: 0; }
503
+
504
+ .page-link {
505
+ min-width: calc(var(--ug-leading) * 1.5);
506
+ display: inline-flex;
507
+ align-items: center;
508
+ justify-content: center;
509
+ border-bottom: 2px solid transparent;
510
+ transition: border-color 0.15s ease-in-out, color 0.15s ease-in-out;
511
+
512
+ &:hover { border-bottom-color: $ug-black; }
513
+ }
514
+
515
+ .page-item.active .page-link,
516
+ .page-item .page-link.active {
517
+ @include ug-font-weight("bold");
518
+ border-bottom-color: $ug-black;
519
+ }
520
+
521
+ .page-item.disabled .page-link { color: $ug-light-gray; }
522
+
523
+ // ==========================================================================
524
+ // Navbar: match .ug-navbar typography and hover
525
+ // ==========================================================================
526
+ .navbar {
527
+ .navbar-brand {
528
+ @include ug-font-size("sm");
529
+ @include ug-font-weight("bold");
530
+ letter-spacing: normal;
531
+ }
532
+
533
+ .navbar-nav {
534
+ .nav-link {
535
+ @include ug-font-size("base");
536
+ @include ug-font-weight("light");
537
+ color: $ug-dark-gray;
538
+ padding: 0 calc(var(--ug-leading) * 0.5);
539
+ transition: color 0.15s, background-color 0.15s;
540
+
541
+ &:hover,
542
+ &:focus {
543
+ color: $ug-black;
544
+ background-color: $ug-warm-gray;
545
+ }
546
+
547
+ &.active,
548
+ &.show {
549
+ color: $ug-black;
550
+ background-color: transparent;
551
+ @include ug-font-weight("light");
552
+ }
553
+ }
554
+ }
555
+
556
+ // ---- Toggler: match .ug-navbar__toggle (text "Menu" / "Close") ----
557
+ // Intentionally does NOT set `display` so Bootstrap's responsive
558
+ // `.navbar-expand-* .navbar-toggler { display: none }` stays in charge.
559
+ .navbar-toggler {
560
+ height: calc(var(--ug-leading) * 1.5);
561
+ padding: 0 calc(var(--ug-leading) * 0.75);
562
+ margin-left: auto;
563
+ @include ug-font-size("sm");
564
+ @include ug-font-weight("bold");
565
+ font-family: inherit;
566
+ letter-spacing: 0.05em;
567
+ line-height: calc(var(--ug-leading) * 1.5);
568
+ color: inherit;
569
+ background: none;
570
+ border: 1px solid currentColor;
571
+ border-radius: 0;
572
+ box-shadow: none;
573
+ cursor: pointer;
574
+ transition: background-color 0.15s, color 0.15s;
575
+ vertical-align: middle;
576
+
577
+ &:focus,
578
+ &:focus-visible {
579
+ box-shadow: none;
580
+ outline: none;
581
+ }
582
+
583
+ &:hover { background-color: rgba(0, 0, 0, 0.05); }
584
+
585
+ .navbar-toggler-icon { display: none; }
586
+
587
+ &::before { content: "Menu"; }
588
+ &[aria-expanded="true"]::before { content: "Close"; }
589
+ }
590
+ }
591
+
592
+ // Dark-variant navbar
593
+ .navbar-dark,
594
+ .navbar[data-bs-theme="dark"] {
595
+ .navbar-brand {
596
+ @include ug-font-size("sm");
597
+ @include ug-font-weight("bold");
598
+ letter-spacing: normal;
599
+ }
600
+
601
+ .navbar-nav .nav-link {
602
+ @include ug-font-size("base");
603
+ @include ug-font-weight("light");
604
+ color: rgba(255, 255, 255, 0.7);
605
+
606
+ &:hover,
607
+ &:focus {
608
+ color: $ug-white;
609
+ background-color: rgba(255, 255, 255, 0.1);
610
+ }
611
+
612
+ &.active,
613
+ &.show {
614
+ color: $ug-white;
615
+ background-color: transparent;
616
+ }
617
+ }
618
+
619
+ .navbar-toggler {
620
+ color: $ug-white;
621
+ border-color: rgba(255, 255, 255, 0.5);
622
+
623
+ &:hover { background-color: rgba(255, 255, 255, 0.1); }
624
+ }
625
+ }
626
+
627
+ // ==========================================================================
628
+ // Bootstrap component adjustments
629
+ // ==========================================================================
630
+ .card {
631
+ border-color: $ug-light-gray;
632
+
633
+ .card-header {
634
+ background-color: $ug-black;
635
+ color: $ug-white;
636
+ @include ug-font-weight("bold");
637
+ border-bottom: none;
638
+ }
639
+ }
640
+
641
+ .modal-header {
642
+ background-color: $ug-black;
643
+ color: $ug-white;
644
+
645
+ .btn-close { filter: invert(1); }
646
+ }
647
+
648
+ .alert {
649
+ border-top: none;
650
+ border-right: none;
651
+ border-bottom: none;
652
+ // border-left stays (3px from $alert-border-width)
653
+ }
654
+
655
+ .table {
656
+ th {
657
+ @include ug-font-size("xs");
658
+ @include ug-font-weight("bold");
659
+ text-transform: uppercase;
660
+ letter-spacing: 0.05em;
661
+ color: $ug-medium-gray;
662
+ }
663
+ }
664
+
665
+ // ==========================================================================
666
+ // Typography tweaks
667
+ // ==========================================================================
668
+ h1, h2, h3, h4, h5, h6 {
669
+ letter-spacing: -0.01em;
670
+ }
671
+
672
+ h1 {
673
+ @include ug-font-weight("black");
674
+ letter-spacing: -0.02em;
675
+ }
676
+
677
+ .lead {
678
+ @include ug-font-weight("light");
679
+ }
680
+
681
+ // ==========================================================================
682
+ // NPS color utilities
683
+ // ==========================================================================
684
+ .bg-brown { background-color: $ug-brown !important; color: $ug-white !important; }
685
+ .bg-nps-green { background-color: $ug-green !important; color: $ug-white !important; }
686
+ .bg-nps-blue { background-color: $ug-blue !important; color: $ug-white !important; }
687
+ .bg-warm-gray { background-color: $ug-warm-gray !important; }
688
+ .text-brown { color: $ug-brown !important; }
689
+ .text-nps-green { color: $ug-green !important; }
690
+ .text-nps-blue { color: $ug-blue !important; }
691
+
692
+ // ==========================================================================
693
+ // Sidebar Layout (.ug-sidebar-layout) — ported from src/scss/_sidebar.scss
694
+ // ==========================================================================
695
+ .ug-sidebar-layout {
696
+ --ug-sidebar-width: calc(var(--ug-leading) * 12);
697
+ display: flex;
698
+ width: 100%;
699
+ min-height: 0;
700
+
701
+ &__sidebar {
702
+ width: var(--ug-sidebar-width);
703
+ flex-shrink: 0;
704
+ padding: var(--ug-leading) calc(var(--ug-leading) * 1.5);
705
+
706
+ @include ug-breakpoint-down("md") {
707
+ display: none;
708
+ }
709
+ }
710
+
711
+ &__main {
712
+ flex: 1;
713
+ min-width: 0;
714
+ padding: var(--ug-leading) calc(var(--ug-leading) * 1.5);
715
+ }
716
+
717
+ &--right { flex-direction: row-reverse; }
718
+
719
+ &--sticky {
720
+ .ug-sidebar-layout__sidebar {
721
+ position: sticky;
722
+ top: 0;
723
+ align-self: flex-start;
724
+ max-height: 100vh;
725
+ overflow-y: auto;
726
+ }
727
+ }
728
+
729
+ &--bordered {
730
+ .ug-sidebar-layout__sidebar {
731
+ border-right: 1px solid $ug-light-gray;
732
+ }
733
+
734
+ &.ug-sidebar-layout--right .ug-sidebar-layout__sidebar {
735
+ border-right: none;
736
+ border-left: 1px solid $ug-light-gray;
737
+ }
738
+ }
739
+
740
+ &__sidebar--visible {
741
+ @include ug-breakpoint-down("md") {
742
+ display: block;
743
+ }
744
+ }
745
+
746
+ // Optional sidebar navigation
747
+ &__nav {
748
+ list-style: none;
749
+ margin: 0;
750
+ padding: 0;
751
+ }
752
+
753
+ &__nav-item { display: block; }
754
+
755
+ &__nav-link {
756
+ display: flex;
757
+ align-items: center;
758
+ padding: calc(var(--ug-leading) * 0.5) 0;
759
+ @include ug-font-size("sm");
760
+ @include ug-rhythm-line-height(1);
761
+ color: $ug-dark-gray;
762
+ text-decoration: none;
763
+ border-bottom: 1px solid $ug-light-gray;
764
+ transition: color 0.15s;
765
+
766
+ &:hover { color: $ug-black; }
767
+
768
+ &--active {
769
+ @include ug-font-weight("bold");
770
+ color: $ug-black;
771
+ }
772
+ }
773
+
774
+ @include ug-breakpoint-down("md") {
775
+ flex-direction: column;
776
+
777
+ &__main {
778
+ padding: var(--ug-leading);
779
+ }
780
+
781
+ &--right { flex-direction: column; }
782
+
783
+ &--bordered .ug-sidebar-layout__sidebar {
784
+ border-right: none;
785
+ border-left: none;
786
+ border-bottom: 1px solid $ug-light-gray;
787
+ }
788
+ }
789
+ }
790
+
791
+ // ==========================================================================
792
+ // Footer (.ug-footer) — ported from src/scss/_footer.scss
793
+ // ==========================================================================
794
+ html, body { min-height: 100vh; }
795
+
796
+ body {
797
+ display: flex;
798
+ flex-direction: column;
799
+
800
+ > *:not(.ug-footer) { flex-shrink: 0; }
801
+ > .ug-footer { margin-top: auto; }
802
+ }
803
+
804
+ .ug-footer {
805
+ width: 100%;
806
+ background-color: $ug-black;
807
+ color: $ug-white;
808
+ padding: calc(var(--ug-leading) * 3) calc(var(--ug-leading) * 1.5);
809
+
810
+ &__inner {
811
+ max-width: $ug-container-max-width;
812
+ margin: 0 auto;
813
+ }
814
+
815
+ &__brand {
816
+ @include ug-font-size("lg");
817
+ @include ug-font-weight("bold");
818
+ @include ug-rhythm-line-height(1);
819
+ @include ug-rhythm-margin-bottom(1);
820
+ color: $ug-white;
821
+ text-decoration: none;
822
+ display: block;
823
+ }
824
+
825
+ &__text {
826
+ @include ug-font-size("sm");
827
+ @include ug-rhythm-line-height(1);
828
+ color: rgba(255, 255, 255, 0.5);
829
+ margin: 0;
830
+ }
831
+
832
+ &__nav {
833
+ display: flex;
834
+ flex-wrap: wrap;
835
+ gap: var(--ug-leading);
836
+ list-style: none;
837
+ margin: 0;
838
+ padding: 0;
839
+ }
840
+
841
+ &__link {
842
+ @include ug-font-size("sm");
843
+ @include ug-rhythm-line-height(1);
844
+ color: rgba(255, 255, 255, 0.7);
845
+ text-decoration: none;
846
+ transition: color 0.15s;
847
+
848
+ &:hover { color: $ug-white; }
849
+ }
850
+
851
+ &__divider {
852
+ border: none;
853
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
854
+ padding-top: calc(var(--ug-leading) - 1px);
855
+ margin: 0;
856
+ }
857
+
858
+ // Compact: single-line footer
859
+ &--compact {
860
+ padding: var(--ug-leading) calc(var(--ug-leading) * 1.5);
861
+
862
+ .ug-footer__inner {
863
+ display: flex;
864
+ align-items: center;
865
+ justify-content: space-between;
866
+ flex-wrap: wrap;
867
+ gap: var(--ug-leading);
868
+ }
869
+
870
+ .ug-footer__brand {
871
+ margin-bottom: 0;
872
+ @include ug-font-size("sm");
873
+ }
874
+
875
+ .ug-footer__nav { margin-bottom: 0; }
876
+ }
877
+
878
+ // Light variant
879
+ &--light {
880
+ background-color: $ug-warm-gray;
881
+ color: $ug-black;
882
+
883
+ .ug-footer__brand { color: $ug-black; }
884
+ .ug-footer__text { color: $ug-medium-gray; }
885
+ .ug-footer__link {
886
+ color: $ug-dark-gray;
887
+ &:hover { color: $ug-black; }
888
+ }
889
+ .ug-footer__divider { border-top-color: $ug-light-gray; }
890
+ }
891
+
892
+ @include ug-breakpoint-down("md") {
893
+ padding: calc(var(--ug-leading) * 2) var(--ug-leading);
894
+
895
+ &--compact {
896
+ padding: var(--ug-leading);
897
+ }
898
+ }
899
+
900
+ @include ug-breakpoint-down("sm") {
901
+ &__nav {
902
+ gap: calc(var(--ug-leading) * 0.5);
903
+ }
904
+ }
905
+ }
@@ -0,0 +1,4 @@
1
+ .container-fluid {
2
+ padding-left: calc(var(--ug-leading) * 1.5);
3
+ padding-right: calc(var(--ug-leading) * 1.5);
4
+ }
@@ -0,0 +1,5 @@
1
+ // ==========================================================================
2
+ // External font imports (plain CSS @import, passed through to the output)
3
+ // ==========================================================================
4
+
5
+ @import url("https://rsms.me/inter/inter.css");
@@ -0,0 +1,36 @@
1
+ // ==========================================================================
2
+ // Unigrid Theme — Helpers
3
+ //
4
+ // Single source of truth for the theme: re-export everything from
5
+ // src/scss/_variables.scss and src/scss/_mixins.scss so the Bootstrap
6
+ // theme reuses the same colors, typography scale, spacing maps, and
7
+ // rhythm mixins as the Unigrid framework itself.
8
+ //
9
+ // Adds a small `leading()` function that returns Sass rem values
10
+ // (usable inside `@use "bootstrap" with (...)` where Bootstrap does
11
+ // compile-time arithmetic on the overridden variables).
12
+ // ==========================================================================
13
+
14
+ @forward "../variables";
15
+ @forward "../mixins";
16
+
17
+ @use "sass:map";
18
+ @use "../variables" as *;
19
+
20
+ /// Convert leading multiples to a rem dimension.
21
+ /// leading(1) -> 1.625rem (one leading unit)
22
+ /// leading(0.25) -> 0.40625rem
23
+ /// leading(1.5) -> 2.4375rem
24
+ @function leading($n) {
25
+ @return $n * $ug-leading-rem * 1rem;
26
+ }
27
+
28
+ /// Shortcut for map.get($ug-font-sizes, $key)
29
+ @function fs($key) {
30
+ @return map.get($ug-font-sizes, $key);
31
+ }
32
+
33
+ /// Shortcut for map.get($ug-font-weights, $key)
34
+ @function fw($key) {
35
+ @return map.get($ug-font-weights, $key);
36
+ }
@@ -0,0 +1,285 @@
1
+ // ==========================================================================
2
+ // Unigrid Theme for Bootstrap 5
3
+ //
4
+ // Applies the Unigrid design language (Massimo Vignelli / NPS) to Bootstrap.
5
+ // All design tokens — colors, typography scale, spacing, rhythm, breakpoints —
6
+ // come from the single source of truth in src/scss (Unigrid framework).
7
+ // This file's job is purely to map those tokens onto Bootstrap's Sass
8
+ // variables so the two systems share identical values.
9
+ //
10
+ // Usage (compiled CSS):
11
+ // <link rel="stylesheet" href="unigrid-bootstrap.css">
12
+ //
13
+ // Usage (SCSS integration — requires bootstrap as npm dependency):
14
+ // @use "unigrid.css/src/scss/bootstrap/unigrid-bootstrap";
15
+ // (compile with --load-path=node_modules)
16
+ // ==========================================================================
17
+
18
+ // ---- External fonts (plain CSS @import, must be emitted first) ----
19
+ @use "fonts";
20
+
21
+ // ---- Unigrid design tokens + rhythm helpers ----
22
+ @use "sass:map";
23
+ @use "helpers" as *;
24
+
25
+ // ---- Bootstrap core, configured from the Unigrid tokens ----
26
+ // leading($n) -> $n × one-leading-unit in rem (0.25, 0.5, 1, 1.5, …)
27
+ // fs($k) -> map.get($ug-font-sizes, $k) (xs, sm, base, lg, xl, …)
28
+ // fw($k) -> map.get($ug-font-weights, $k) (light, regular, bold, …)
29
+ @use "bootstrap/scss/bootstrap" with (
30
+ // Opt in to Bootstrap's native CSS Grid layout (.grid / g-col-*)
31
+ $enable-cssgrid: true,
32
+ $enable-grid-classes: false,
33
+
34
+ // ---- Colors (from $ug-* palette) ----
35
+ $primary: $ug-black,
36
+ $secondary: $ug-medium-gray,
37
+ $success: $ug-green,
38
+ $danger: $ug-red,
39
+ $warning: #d4a017,
40
+ $info: $ug-blue,
41
+ $light: $ug-warm-gray,
42
+ $dark: $ug-black,
43
+
44
+ $body-bg: $ug-white,
45
+ $body-color: $ug-black,
46
+
47
+ // ---- Typography (from $ug-font-* maps) ----
48
+ $font-family-sans-serif: $ug-font-family,
49
+ $font-family-base: $ug-font-family,
50
+
51
+ $font-size-base: fs("base"), // 1rem
52
+ $font-size-sm: fs("sm"), // 0.875rem
53
+ $font-size-lg: fs("lg"), // 1.25rem
54
+ $small-font-size: fs("xs"), // 0.75rem
55
+
56
+ $font-weight-light: fw("light"), // 300
57
+ $font-weight-normal: fw("regular"), // 400
58
+ $font-weight-bold: fw("bold"), // 700
59
+ $font-weight-bolder: fw("black"), // 900
60
+
61
+ $line-height-base: $ug-base-leading, // 1.625
62
+ $line-height-sm: $ug-line-height-tight, // 1.2
63
+ $line-height-lg: $ug-base-leading-desktop, // 1.7
64
+
65
+ $headings-font-weight: fw("bold"),
66
+ $headings-line-height: $ug-line-height-tight,
67
+ $headings-margin-bottom: 0.5em,
68
+
69
+ // Heading font sizes come from the $ug-headings Gutenberg table
70
+ // (first tuple value = rem).
71
+ $h1-font-size: 2.5rem,
72
+ $h2-font-size: 1.6875rem,
73
+ $h3-font-size: 1.375rem,
74
+ $h4-font-size: 1.2rem,
75
+ $h5-font-size: fs("base"),
76
+ $h6-font-size: fs("base"),
77
+
78
+ $lead-font-size: 1.2rem,
79
+ $lead-font-weight: fw("light"),
80
+
81
+ // ---- Links ----
82
+ $link-color: $ug-black,
83
+ $link-decoration: underline,
84
+
85
+ // ---- Border radius: sharp corners from $ug-border-radius ----
86
+ $border-radius: $ug-border-radius,
87
+ $border-radius-sm: $ug-border-radius-sm,
88
+ $border-radius-lg: $ug-border-radius-lg,
89
+ $border-radius-xl: $ug-border-radius,
90
+ $border-radius-xxl: $ug-border-radius,
91
+ $border-radius-pill: $ug-border-radius-pill,
92
+
93
+ // ---- Spacing ----
94
+ // $spacer = one leading unit — consumed by Bootstrap internals that do
95
+ // Sass arithmetic on it ($navbar-padding-y: $spacer * .5, etc.), so it
96
+ // must stay a real rem dimension.
97
+ $spacer: leading(1),
98
+
99
+ // ---- Spacer utilities ----
100
+ // .p-*/.m-*/.gap-*/.row-gap-*/.col-gap-* must snap to Unigrid's live
101
+ // rhythm so they adapt at the desktop breakpoint (26px → 31px) in sync
102
+ // with the .ug-* components. Override the full map with calc(var())
103
+ // expressions so the values are computed at runtime against --ug-leading
104
+ // rather than being baked-in rem at compile time.
105
+ $spacers: (
106
+ 0: 0,
107
+ 1: calc(var(--ug-leading) * 0.25), // 0.25 leading
108
+ 2: calc(var(--ug-leading) * 0.5), // 0.5 leading
109
+ 3: calc(var(--ug-leading) * 1), // 1 leading
110
+ 4: calc(var(--ug-leading) * 1.5), // 1.5 leading
111
+ 5: calc(var(--ug-leading) * 3), // 3 leading
112
+ ),
113
+
114
+ // Grid gutters (.g-*, .gx-*, .gy-*) share the same rhythm scale.
115
+ $gutters: (
116
+ 0: 0,
117
+ 1: calc(var(--ug-leading) * 0.25),
118
+ 2: calc(var(--ug-leading) * 0.5),
119
+ 3: calc(var(--ug-leading) * 1),
120
+ 4: calc(var(--ug-leading) * 1.5),
121
+ 5: calc(var(--ug-leading) * 3),
122
+ ),
123
+
124
+ $paragraph-margin-bottom: calc(var(--ug-leading) * 1),
125
+
126
+ // ---- Buttons ----
127
+ $btn-font-weight: fw("bold"),
128
+ $btn-padding-y: leading(0.25),
129
+ $btn-padding-x: leading(1),
130
+ $btn-padding-y-sm: leading(0.125),
131
+ $btn-padding-x-sm: leading(0.5),
132
+ $btn-padding-y-lg: leading(0.5),
133
+ $btn-padding-x-lg: leading(1.5),
134
+ $btn-border-width: 2px,
135
+
136
+ // ---- Cards ----
137
+ $card-border-color: $ug-light-gray,
138
+ $card-border-width: 1px,
139
+ $card-cap-bg: $ug-warm-gray,
140
+ $card-spacer-y: leading(1),
141
+ $card-spacer-x: leading(1),
142
+ $card-cap-padding-y: leading(0.5),
143
+ $card-cap-padding-x: leading(1),
144
+
145
+ // ---- Inputs ----
146
+ $input-padding-y: leading(0.25),
147
+ $input-padding-x: leading(0.5),
148
+ $input-padding-y-sm: leading(0.125),
149
+ $input-padding-x-sm: leading(0.25),
150
+ $input-padding-y-lg: leading(0.5),
151
+ $input-padding-x-lg: leading(0.75),
152
+ $input-border-color: $ug-light-gray,
153
+ $input-focus-border-color: $ug-black,
154
+ $input-focus-box-shadow: none,
155
+ $input-btn-focus-box-shadow: none,
156
+
157
+ // ---- Navbar ----
158
+ $navbar-padding-y: leading(0.5),
159
+ $navbar-nav-link-padding-x: leading(0.5),
160
+ $navbar-brand-padding-y: leading(0.25),
161
+ $navbar-dark-color: rgba(255, 255, 255, 0.7),
162
+ $navbar-dark-hover-color: $ug-white,
163
+ $navbar-dark-active-color: $ug-white,
164
+ $navbar-dark-toggler-border-color: rgba(255, 255, 255, 0.5),
165
+
166
+ // ---- Nav (tabs / pills) ----
167
+ $nav-link-padding-y: leading(0.25),
168
+ $nav-link-padding-x: leading(0.5),
169
+
170
+ // ---- Tables ----
171
+ $table-cell-padding-y: leading(0.25),
172
+ $table-cell-padding-x: leading(0.25),
173
+ $table-cell-padding-y-sm: leading(0.125),
174
+ $table-cell-padding-x-sm: leading(0.125),
175
+ $table-border-color: $ug-light-gray,
176
+ $table-striped-bg: $ug-warm-gray,
177
+
178
+ // ---- Modals ----
179
+ $modal-inner-padding: leading(1),
180
+ $modal-header-padding-y: leading(1),
181
+ $modal-header-padding-x: leading(1),
182
+ $modal-content-border-width: 0,
183
+ $modal-header-border-width: 0,
184
+ $modal-footer-border-width: 1px,
185
+ $modal-footer-border-color: $ug-light-gray,
186
+
187
+ // ---- Dropdowns ----
188
+ $dropdown-padding-y: leading(0.25),
189
+ $dropdown-padding-x: 0,
190
+ $dropdown-item-padding-y: leading(0.25),
191
+ $dropdown-item-padding-x: leading(1),
192
+ $dropdown-border-color: $ug-light-gray,
193
+ $dropdown-link-color: $ug-dark-gray,
194
+ $dropdown-link-hover-bg: $ug-warm-gray,
195
+ $dropdown-link-hover-color: $ug-black,
196
+
197
+ // ---- Accordions ----
198
+ $accordion-padding-y: leading(0.5),
199
+ $accordion-padding-x: leading(0.75),
200
+ $accordion-border-color: $ug-light-gray,
201
+ $accordion-button-active-bg: transparent,
202
+ $accordion-button-active-color: $ug-black,
203
+
204
+ // ---- Pagination (flat Unigrid style) ----
205
+ $pagination-padding-y: leading(0.25),
206
+ $pagination-padding-x: leading(0.5),
207
+ $pagination-padding-y-sm: leading(0.125),
208
+ $pagination-padding-x-sm: leading(0.25),
209
+ $pagination-padding-y-lg: leading(0.5),
210
+ $pagination-padding-x-lg: leading(1),
211
+ $pagination-color: $ug-black,
212
+ $pagination-bg: transparent,
213
+ $pagination-border-width: 0,
214
+ $pagination-border-color: transparent,
215
+ $pagination-hover-color: $ug-black,
216
+ $pagination-hover-bg: transparent,
217
+ $pagination-hover-border-color: transparent,
218
+ $pagination-focus-color: $ug-black,
219
+ $pagination-focus-bg: transparent,
220
+ $pagination-focus-box-shadow: none,
221
+ $pagination-active-color: $ug-black,
222
+ $pagination-active-bg: transparent,
223
+ $pagination-active-border-color: transparent,
224
+ $pagination-disabled-color: $ug-light-gray,
225
+ $pagination-disabled-bg: transparent,
226
+ $pagination-disabled-border-color: transparent,
227
+
228
+ // ---- Breadcrumb ----
229
+ $breadcrumb-divider-color: $ug-medium-gray,
230
+ $breadcrumb-active-color: $ug-medium-gray,
231
+ $breadcrumb-margin-bottom: leading(1),
232
+ $breadcrumb-padding-y: 0,
233
+ $breadcrumb-padding-x: 0,
234
+
235
+ // ---- Badge ----
236
+ $badge-font-weight: fw("bold"),
237
+ $badge-font-size: 0.7rem,
238
+ $badge-padding-y: leading(0.125),
239
+ $badge-padding-x: leading(0.25),
240
+
241
+ // ---- Alert ----
242
+ $alert-border-width: 3px,
243
+ $alert-padding-y: leading(1),
244
+ $alert-padding-x: leading(1),
245
+ $alert-margin-bottom: leading(1),
246
+
247
+ // ---- List group ----
248
+ $list-group-item-padding-y: leading(0.5),
249
+ $list-group-item-padding-x: leading(1),
250
+
251
+ // ---- Toast / popover / tooltip ----
252
+ $toast-padding-x: leading(0.5),
253
+ $toast-padding-y: leading(0.25),
254
+ $popover-body-padding-y: leading(0.5),
255
+ $popover-body-padding-x: leading(0.5),
256
+ $tooltip-padding-y: leading(0.125),
257
+ $tooltip-padding-x: leading(0.25),
258
+
259
+ // ---- Progress bars ----
260
+ $progress-height: leading(0.5),
261
+
262
+ // ---- Offcanvas ----
263
+ $offcanvas-padding-y: leading(1),
264
+ $offcanvas-padding-x: leading(1),
265
+
266
+ // ---- Forms ----
267
+ $form-label-margin-bottom: 0,
268
+ $form-label-font-weight: fw("bold"),
269
+ $form-text-margin-top: leading(0.25),
270
+
271
+ // ---- Grid ----
272
+ $grid-gutter-width: leading(1),
273
+
274
+ // ---- Shadows: none (flat design) ----
275
+ $box-shadow: none,
276
+ $box-shadow-sm: none,
277
+ $box-shadow-lg: none,
278
+
279
+ // ---- Transitions ----
280
+ $transition-base: all 0.15s ease-in-out
281
+ );
282
+
283
+ // ---- Unigrid custom components (layered on top of Bootstrap) ----
284
+ @use "components";
285
+ @use "containers";