unigrid.css 0.3.0 → 0.3.2

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 (44) hide show
  1. package/README.md +101 -101
  2. package/dist/dropdown.js +36 -36
  3. package/dist/scrollspy.js +57 -57
  4. package/dist/tabs.js +30 -30
  5. package/dist/unigrid.css +4608 -4501
  6. package/dist/unigrid.js +173 -124
  7. package/dist/unigrid.min.css +1 -1
  8. package/dist/unigrid.min.js +1 -1
  9. package/package.json +70 -41
  10. package/src/js/dropdown.js +49 -49
  11. package/src/js/index.js +20 -19
  12. package/src/js/modal.js +81 -0
  13. package/src/js/scrollspy.js +87 -87
  14. package/src/js/tabs.js +58 -58
  15. package/src/scss/_accordion.scss +123 -123
  16. package/src/scss/_broadside.scss +125 -125
  17. package/src/scss/_buttons.scss +241 -241
  18. package/src/scss/_card.scss +168 -168
  19. package/src/scss/_components.scss +140 -140
  20. package/src/scss/_container.scss +53 -53
  21. package/src/scss/_dropdown.scss +178 -178
  22. package/src/scss/_footer.scss +147 -147
  23. package/src/scss/_formats.scss +64 -64
  24. package/src/scss/_forms.scss +192 -192
  25. package/src/scss/_grid.scss +114 -114
  26. package/src/scss/_header.scss +169 -169
  27. package/src/scss/_hero.scss +262 -262
  28. package/src/scss/_mixins.scss +120 -120
  29. package/src/scss/_modal.scss +158 -0
  30. package/src/scss/_modules.scss +238 -238
  31. package/src/scss/_navbar.scss +341 -341
  32. package/src/scss/_pagination.scss +160 -160
  33. package/src/scss/_prose.scss +393 -393
  34. package/src/scss/_reset.scss +82 -82
  35. package/src/scss/_scrollspy.scss +62 -62
  36. package/src/scss/_section.scss +91 -91
  37. package/src/scss/_sidebar.scss +147 -147
  38. package/src/scss/_table.scss +122 -122
  39. package/src/scss/_tabs.scss +178 -178
  40. package/src/scss/_typography.scss +105 -105
  41. package/src/scss/_utilities.scss +79 -79
  42. package/src/scss/_variables.scss +183 -183
  43. package/src/scss/unigrid.scss +50 -49
  44. package/dist/index.js +0 -5
package/src/js/index.js CHANGED
@@ -1,19 +1,20 @@
1
- /**
2
- * Unigrid.js — All interactive components bundled.
3
- *
4
- * Includes: dropdown, tabs, scrollspy
5
- *
6
- * Usage:
7
- * <script src="dist/unigrid.js"></script>
8
- * or
9
- * <script src="dist/unigrid.min.js"></script>
10
- *
11
- * Individual files also available:
12
- * <script src="dist/dropdown.js"></script>
13
- * <script src="dist/tabs.js"></script>
14
- * <script src="dist/scrollspy.js"></script>
15
- */
16
-
17
- import './dropdown.js';
18
- import './tabs.js';
19
- import './scrollspy.js';
1
+ /**
2
+ * Unigrid.js — All interactive components bundled.
3
+ *
4
+ * Includes: dropdown, tabs, scrollspy
5
+ *
6
+ * Usage:
7
+ * <script src="dist/unigrid.js"></script>
8
+ * or
9
+ * <script src="dist/unigrid.min.js"></script>
10
+ *
11
+ * Individual files also available:
12
+ * <script src="dist/dropdown.js"></script>
13
+ * <script src="dist/tabs.js"></script>
14
+ * <script src="dist/scrollspy.js"></script>
15
+ */
16
+
17
+ import './dropdown.js';
18
+ import './tabs.js';
19
+ import './scrollspy.js';
20
+ import './modal.js';
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Unigrid Modal
3
+ *
4
+ * Opens/closes modals via data attributes.
5
+ * Closes on backdrop click, close button, or Escape key.
6
+ * Locks body scroll when open.
7
+ *
8
+ * Usage:
9
+ * <button data-ug-modal-open="my-modal">Open</button>
10
+ *
11
+ * <div class="ug-modal" id="my-modal">
12
+ * <div class="ug-modal__backdrop" data-ug-modal-close></div>
13
+ * <div class="ug-modal__dialog">
14
+ * <div class="ug-modal__header">
15
+ * <h3 class="ug-modal__title">Title</h3>
16
+ * <button class="ug-modal__close" data-ug-modal-close>&times;</button>
17
+ * </div>
18
+ * <div class="ug-modal__body">Content</div>
19
+ * <div class="ug-modal__footer">
20
+ * <button class="ug-btn" data-ug-modal-close>Close</button>
21
+ * </div>
22
+ * </div>
23
+ * </div>
24
+ */
25
+ (function () {
26
+ function openModal(id) {
27
+ var modal = document.getElementById(id);
28
+ if (!modal) return;
29
+ modal.classList.add('ug-modal--open');
30
+ document.body.classList.add('ug-modal-open');
31
+ }
32
+
33
+ function closeModal(modal) {
34
+ if (!modal) return;
35
+ modal.classList.remove('ug-modal--open');
36
+ // Only unlock body if no other modals are open
37
+ if (!document.querySelector('.ug-modal--open')) {
38
+ document.body.classList.remove('ug-modal-open');
39
+ }
40
+ }
41
+
42
+ function closeAllModals() {
43
+ document.querySelectorAll('.ug-modal--open').forEach(function (m) {
44
+ m.classList.remove('ug-modal--open');
45
+ });
46
+ document.body.classList.remove('ug-modal-open');
47
+ }
48
+
49
+ function init() {
50
+ // Open triggers
51
+ document.addEventListener('click', function (e) {
52
+ var openTrigger = e.target.closest('[data-ug-modal-open]');
53
+ if (openTrigger) {
54
+ e.preventDefault();
55
+ openModal(openTrigger.getAttribute('data-ug-modal-open'));
56
+ return;
57
+ }
58
+
59
+ // Close triggers
60
+ var closeTrigger = e.target.closest('[data-ug-modal-close]');
61
+ if (closeTrigger) {
62
+ e.preventDefault();
63
+ var modal = closeTrigger.closest('.ug-modal');
64
+ closeModal(modal);
65
+ }
66
+ });
67
+
68
+ // Close on Escape
69
+ document.addEventListener('keydown', function (e) {
70
+ if (e.key === 'Escape') {
71
+ closeAllModals();
72
+ }
73
+ });
74
+ }
75
+
76
+ if (document.readyState === 'loading') {
77
+ document.addEventListener('DOMContentLoaded', init);
78
+ } else {
79
+ init();
80
+ }
81
+ })();
@@ -1,87 +1,87 @@
1
- /**
2
- * Unigrid Scrollspy
3
- *
4
- * Highlights navigation links based on which section is currently
5
- * visible in the viewport using IntersectionObserver.
6
- *
7
- * Usage:
8
- * <nav data-ug-scrollspy>
9
- * <a class="ug-scrollspy__link" href="#intro">Intro</a>
10
- * <a class="ug-scrollspy__link" href="#features">Features</a>
11
- * </nav>
12
- *
13
- * <section id="intro">...</section>
14
- * <section id="features">...</section>
15
- *
16
- * Options (via data attributes on [data-ug-scrollspy]):
17
- * data-ug-scrollspy-offset="100" — top offset in px (default: 100)
18
- * data-ug-scrollspy-class="custom" — active class (default: ug-scrollspy__link--active)
19
- */
20
- (function () {
21
- function init() {
22
- document.querySelectorAll('[data-ug-scrollspy]').forEach(function (nav) {
23
- var links = nav.querySelectorAll('.ug-scrollspy__link, [data-ug-spy]');
24
- if (!links.length) return;
25
-
26
- var offset = parseInt(nav.getAttribute('data-ug-scrollspy-offset') || '100', 10);
27
- var activeClass = nav.getAttribute('data-ug-scrollspy-class') || 'ug-scrollspy__link--active';
28
-
29
- // Collect target sections
30
- var targets = [];
31
- links.forEach(function (link) {
32
- var href = link.getAttribute('href');
33
- if (!href || href.charAt(0) !== '#') return;
34
- var target = document.querySelector(href);
35
- if (target) {
36
- targets.push({ link: link, target: target });
37
- }
38
- });
39
-
40
- if (!targets.length) return;
41
-
42
- // Track which sections are visible
43
- var visibleSections = new Set();
44
-
45
- var observer = new IntersectionObserver(function (entries) {
46
- entries.forEach(function (entry) {
47
- if (entry.isIntersecting) {
48
- visibleSections.add(entry.target.id);
49
- } else {
50
- visibleSections.delete(entry.target.id);
51
- }
52
- });
53
-
54
- // Find the first visible section (in DOM order)
55
- var activeId = null;
56
- for (var i = 0; i < targets.length; i++) {
57
- if (visibleSections.has(targets[i].target.id)) {
58
- activeId = targets[i].target.id;
59
- break;
60
- }
61
- }
62
-
63
- // Update active states
64
- targets.forEach(function (item) {
65
- if (item.target.id === activeId) {
66
- item.link.classList.add(activeClass);
67
- } else {
68
- item.link.classList.remove(activeClass);
69
- }
70
- });
71
- }, {
72
- rootMargin: '-' + offset + 'px 0px -50% 0px',
73
- threshold: 0
74
- });
75
-
76
- targets.forEach(function (item) {
77
- observer.observe(item.target);
78
- });
79
- });
80
- }
81
-
82
- if (document.readyState === 'loading') {
83
- document.addEventListener('DOMContentLoaded', init);
84
- } else {
85
- init();
86
- }
87
- })();
1
+ /**
2
+ * Unigrid Scrollspy
3
+ *
4
+ * Highlights navigation links based on which section is currently
5
+ * visible in the viewport using IntersectionObserver.
6
+ *
7
+ * Usage:
8
+ * <nav data-ug-scrollspy>
9
+ * <a class="ug-scrollspy__link" href="#intro">Intro</a>
10
+ * <a class="ug-scrollspy__link" href="#features">Features</a>
11
+ * </nav>
12
+ *
13
+ * <section id="intro">...</section>
14
+ * <section id="features">...</section>
15
+ *
16
+ * Options (via data attributes on [data-ug-scrollspy]):
17
+ * data-ug-scrollspy-offset="100" — top offset in px (default: 100)
18
+ * data-ug-scrollspy-class="custom" — active class (default: ug-scrollspy__link--active)
19
+ */
20
+ (function () {
21
+ function init() {
22
+ document.querySelectorAll('[data-ug-scrollspy]').forEach(function (nav) {
23
+ var links = nav.querySelectorAll('.ug-scrollspy__link, [data-ug-spy]');
24
+ if (!links.length) return;
25
+
26
+ var offset = parseInt(nav.getAttribute('data-ug-scrollspy-offset') || '100', 10);
27
+ var activeClass = nav.getAttribute('data-ug-scrollspy-class') || 'ug-scrollspy__link--active';
28
+
29
+ // Collect target sections
30
+ var targets = [];
31
+ links.forEach(function (link) {
32
+ var href = link.getAttribute('href');
33
+ if (!href || href.charAt(0) !== '#') return;
34
+ var target = document.querySelector(href);
35
+ if (target) {
36
+ targets.push({ link: link, target: target });
37
+ }
38
+ });
39
+
40
+ if (!targets.length) return;
41
+
42
+ // Track which sections are visible
43
+ var visibleSections = new Set();
44
+
45
+ var observer = new IntersectionObserver(function (entries) {
46
+ entries.forEach(function (entry) {
47
+ if (entry.isIntersecting) {
48
+ visibleSections.add(entry.target.id);
49
+ } else {
50
+ visibleSections.delete(entry.target.id);
51
+ }
52
+ });
53
+
54
+ // Find the first visible section (in DOM order)
55
+ var activeId = null;
56
+ for (var i = 0; i < targets.length; i++) {
57
+ if (visibleSections.has(targets[i].target.id)) {
58
+ activeId = targets[i].target.id;
59
+ break;
60
+ }
61
+ }
62
+
63
+ // Update active states
64
+ targets.forEach(function (item) {
65
+ if (item.target.id === activeId) {
66
+ item.link.classList.add(activeClass);
67
+ } else {
68
+ item.link.classList.remove(activeClass);
69
+ }
70
+ });
71
+ }, {
72
+ rootMargin: '-' + offset + 'px 0px -50% 0px',
73
+ threshold: 0
74
+ });
75
+
76
+ targets.forEach(function (item) {
77
+ observer.observe(item.target);
78
+ });
79
+ });
80
+ }
81
+
82
+ if (document.readyState === 'loading') {
83
+ document.addEventListener('DOMContentLoaded', init);
84
+ } else {
85
+ init();
86
+ }
87
+ })();
package/src/js/tabs.js CHANGED
@@ -1,58 +1,58 @@
1
- /**
2
- * Unigrid Tabs
3
- *
4
- * Switches active tab link and panel on click.
5
- * Auto-initializes on DOMContentLoaded.
6
- *
7
- * Usage:
8
- * <div class="ug-tabs" data-ug-tabs>
9
- * <ul class="ug-tabs__nav">
10
- * <li class="ug-tabs__item">
11
- * <button class="ug-tabs__link ug-tabs__link--active" data-ug-tab="tab1">Tab 1</button>
12
- * </li>
13
- * </ul>
14
- * <div class="ug-tabs__content">
15
- * <div class="ug-tabs__panel ug-tabs__panel--active" data-ug-panel="tab1">...</div>
16
- * </div>
17
- * </div>
18
- */
19
- (function () {
20
- function init() {
21
- document.addEventListener('click', function (e) {
22
- var link = e.target.closest('[data-ug-tab]');
23
- if (!link) return;
24
-
25
- e.preventDefault();
26
-
27
- var tabs = link.closest('.ug-tabs');
28
- if (!tabs) return;
29
-
30
- var target = link.getAttribute('data-ug-tab');
31
-
32
- // Deactivate all links
33
- tabs.querySelectorAll('.ug-tabs__link').forEach(function (l) {
34
- l.classList.remove('ug-tabs__link--active');
35
- });
36
-
37
- // Deactivate all panels
38
- tabs.querySelectorAll('.ug-tabs__panel').forEach(function (p) {
39
- p.classList.remove('ug-tabs__panel--active');
40
- });
41
-
42
- // Activate clicked link
43
- link.classList.add('ug-tabs__link--active');
44
-
45
- // Activate matching panel
46
- var panel = tabs.querySelector('[data-ug-panel="' + target + '"]');
47
- if (panel) {
48
- panel.classList.add('ug-tabs__panel--active');
49
- }
50
- });
51
- }
52
-
53
- if (document.readyState === 'loading') {
54
- document.addEventListener('DOMContentLoaded', init);
55
- } else {
56
- init();
57
- }
58
- })();
1
+ /**
2
+ * Unigrid Tabs
3
+ *
4
+ * Switches active tab link and panel on click.
5
+ * Auto-initializes on DOMContentLoaded.
6
+ *
7
+ * Usage:
8
+ * <div class="ug-tabs" data-ug-tabs>
9
+ * <ul class="ug-tabs__nav">
10
+ * <li class="ug-tabs__item">
11
+ * <button class="ug-tabs__link ug-tabs__link--active" data-ug-tab="tab1">Tab 1</button>
12
+ * </li>
13
+ * </ul>
14
+ * <div class="ug-tabs__content">
15
+ * <div class="ug-tabs__panel ug-tabs__panel--active" data-ug-panel="tab1">...</div>
16
+ * </div>
17
+ * </div>
18
+ */
19
+ (function () {
20
+ function init() {
21
+ document.addEventListener('click', function (e) {
22
+ var link = e.target.closest('[data-ug-tab]');
23
+ if (!link) return;
24
+
25
+ e.preventDefault();
26
+
27
+ var tabs = link.closest('.ug-tabs');
28
+ if (!tabs) return;
29
+
30
+ var target = link.getAttribute('data-ug-tab');
31
+
32
+ // Deactivate all links
33
+ tabs.querySelectorAll('.ug-tabs__link').forEach(function (l) {
34
+ l.classList.remove('ug-tabs__link--active');
35
+ });
36
+
37
+ // Deactivate all panels
38
+ tabs.querySelectorAll('.ug-tabs__panel').forEach(function (p) {
39
+ p.classList.remove('ug-tabs__panel--active');
40
+ });
41
+
42
+ // Activate clicked link
43
+ link.classList.add('ug-tabs__link--active');
44
+
45
+ // Activate matching panel
46
+ var panel = tabs.querySelector('[data-ug-panel="' + target + '"]');
47
+ if (panel) {
48
+ panel.classList.add('ug-tabs__panel--active');
49
+ }
50
+ });
51
+ }
52
+
53
+ if (document.readyState === 'loading') {
54
+ document.addEventListener('DOMContentLoaded', init);
55
+ } else {
56
+ init();
57
+ }
58
+ })();
@@ -1,123 +1,123 @@
1
- // ==========================================================================
2
- // Unigrid CSS Framework — Accordion (BEM)
3
- //
4
- // Pure HTML/CSS accordion using <details>/<summary>. No JS required.
5
- // Caret rotates when open.
6
- //
7
- // Block: .ug-accordion
8
- // Elements: __item, __header, __caret, __body
9
- // Modifiers: --bordered, --flush, --dark
10
- // ==========================================================================
11
-
12
- @use "variables" as *;
13
- @use "mixins" as *;
14
-
15
- .ug-accordion {
16
-
17
- // ---- Item (details element) ----
18
- &__item {
19
- border-bottom: 1px solid $ug-light-gray;
20
-
21
- &[open] > .ug-accordion__header .ug-accordion__caret {
22
- transform: rotate(-135deg);
23
- }
24
- }
25
-
26
- // ---- Header (summary element) ----
27
- &__header {
28
- display: flex;
29
- align-items: center;
30
- justify-content: space-between;
31
- padding: calc(var(--ug-leading) * 0.5) 0;
32
- @include ug-font-size("base");
33
- @include ug-font-weight("bold");
34
- @include ug-rhythm-line-height(1);
35
- cursor: pointer;
36
- list-style: none;
37
- user-select: none;
38
- transition: color 0.15s;
39
-
40
- &::-webkit-details-marker {
41
- display: none;
42
- }
43
-
44
- &::marker {
45
- content: "";
46
- }
47
-
48
- &:hover {
49
- color: $ug-medium-gray;
50
- }
51
- }
52
-
53
- // ---- Caret ----
54
- &__caret {
55
- display: inline-block;
56
- width: 0.6em;
57
- height: 0.6em;
58
- border-right: 2px solid currentColor;
59
- border-bottom: 2px solid currentColor;
60
- transform: rotate(45deg);
61
- transition: transform 0.2s;
62
- flex-shrink: 0;
63
- margin-left: var(--ug-leading);
64
- }
65
-
66
- // ---- Body ----
67
- &__body {
68
- padding: 0 0 var(--ug-leading) 0;
69
- @include ug-font-size("sm");
70
- @include ug-rhythm-line-height(1);
71
- color: $ug-dark-gray;
72
- }
73
-
74
- // ==============================
75
- // Modifiers
76
- // ==============================
77
-
78
- // Bordered: border on all sides
79
- &--bordered {
80
- border: 1px solid $ug-light-gray;
81
-
82
- .ug-accordion__item {
83
- border-bottom: 1px solid $ug-light-gray;
84
-
85
- &:last-child {
86
- border-bottom: none;
87
- }
88
- }
89
-
90
- .ug-accordion__header {
91
- padding: calc(var(--ug-leading) * 0.5) var(--ug-leading);
92
- }
93
-
94
- .ug-accordion__body {
95
- padding: 0 var(--ug-leading) var(--ug-leading);
96
- }
97
- }
98
-
99
- // Flush: no borders at all
100
- &--flush {
101
- .ug-accordion__item {
102
- border-bottom: none;
103
- }
104
- }
105
-
106
- // Dark
107
- &--dark {
108
- background-color: $ug-black;
109
- color: $ug-white;
110
-
111
- .ug-accordion__item {
112
- border-bottom-color: rgba(255, 255, 255, 0.1);
113
- }
114
-
115
- .ug-accordion__header:hover {
116
- color: rgba(255, 255, 255, 0.6);
117
- }
118
-
119
- .ug-accordion__body {
120
- color: rgba(255, 255, 255, 0.7);
121
- }
122
- }
123
- }
1
+ // ==========================================================================
2
+ // Unigrid CSS Framework — Accordion (BEM)
3
+ //
4
+ // Pure HTML/CSS accordion using <details>/<summary>. No JS required.
5
+ // Caret rotates when open.
6
+ //
7
+ // Block: .ug-accordion
8
+ // Elements: __item, __header, __caret, __body
9
+ // Modifiers: --bordered, --flush, --dark
10
+ // ==========================================================================
11
+
12
+ @use "variables" as *;
13
+ @use "mixins" as *;
14
+
15
+ .ug-accordion {
16
+
17
+ // ---- Item (details element) ----
18
+ &__item {
19
+ border-bottom: 1px solid $ug-light-gray;
20
+
21
+ &[open] > .ug-accordion__header .ug-accordion__caret {
22
+ transform: rotate(-135deg);
23
+ }
24
+ }
25
+
26
+ // ---- Header (summary element) ----
27
+ &__header {
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: space-between;
31
+ padding: calc(var(--ug-leading) * 0.5) 0;
32
+ @include ug-font-size("base");
33
+ @include ug-font-weight("bold");
34
+ @include ug-rhythm-line-height(1);
35
+ cursor: pointer;
36
+ list-style: none;
37
+ user-select: none;
38
+ transition: color 0.15s;
39
+
40
+ &::-webkit-details-marker {
41
+ display: none;
42
+ }
43
+
44
+ &::marker {
45
+ content: "";
46
+ }
47
+
48
+ &:hover {
49
+ color: $ug-medium-gray;
50
+ }
51
+ }
52
+
53
+ // ---- Caret ----
54
+ &__caret {
55
+ display: inline-block;
56
+ width: 0.6em;
57
+ height: 0.6em;
58
+ border-right: 2px solid currentColor;
59
+ border-bottom: 2px solid currentColor;
60
+ transform: rotate(45deg);
61
+ transition: transform 0.2s;
62
+ flex-shrink: 0;
63
+ margin-left: var(--ug-leading);
64
+ }
65
+
66
+ // ---- Body ----
67
+ &__body {
68
+ padding: 0 0 var(--ug-leading) 0;
69
+ @include ug-font-size("sm");
70
+ @include ug-rhythm-line-height(1);
71
+ color: $ug-dark-gray;
72
+ }
73
+
74
+ // ==============================
75
+ // Modifiers
76
+ // ==============================
77
+
78
+ // Bordered: border on all sides
79
+ &--bordered {
80
+ border: 1px solid $ug-light-gray;
81
+
82
+ .ug-accordion__item {
83
+ border-bottom: 1px solid $ug-light-gray;
84
+
85
+ &:last-child {
86
+ border-bottom: none;
87
+ }
88
+ }
89
+
90
+ .ug-accordion__header {
91
+ padding: calc(var(--ug-leading) * 0.5) var(--ug-leading);
92
+ }
93
+
94
+ .ug-accordion__body {
95
+ padding: 0 var(--ug-leading) var(--ug-leading);
96
+ }
97
+ }
98
+
99
+ // Flush: no borders at all
100
+ &--flush {
101
+ .ug-accordion__item {
102
+ border-bottom: none;
103
+ }
104
+ }
105
+
106
+ // Dark
107
+ &--dark {
108
+ background-color: $ug-black;
109
+ color: $ug-white;
110
+
111
+ .ug-accordion__item {
112
+ border-bottom-color: rgba(255, 255, 255, 0.1);
113
+ }
114
+
115
+ .ug-accordion__header:hover {
116
+ color: rgba(255, 255, 255, 0.6);
117
+ }
118
+
119
+ .ug-accordion__body {
120
+ color: rgba(255, 255, 255, 0.7);
121
+ }
122
+ }
123
+ }