ultimate-jekyll-manager 1.4.3 → 1.5.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/CLAUDE-ATTRIBUTION.md +215 -0
  3. package/CLAUDE.md +7 -6
  4. package/README.md +1 -0
  5. package/dist/assets/css/pages/test/libraries/layers/index.scss +28 -0
  6. package/dist/assets/js/modules/redirect.js +5 -4
  7. package/dist/assets/js/pages/download/index.js +1 -1
  8. package/dist/assets/js/pages/feedback/index.js +7 -1
  9. package/dist/assets/js/pages/test/libraries/layers/index.js +11 -0
  10. package/dist/assets/themes/_template/README.md +50 -0
  11. package/dist/assets/themes/_template/_config.scss +60 -0
  12. package/dist/assets/themes/_template/_theme.js +13 -4
  13. package/dist/assets/themes/_template/_theme.scss +16 -4
  14. package/dist/assets/themes/_template/css/base/_root.scss +19 -0
  15. package/dist/assets/themes/_template/css/components/_components.scss +23 -0
  16. package/dist/assets/themes/classy/README.md +18 -6
  17. package/dist/assets/themes/neobrutalism/README.md +98 -0
  18. package/dist/assets/themes/neobrutalism/_config.scss +139 -0
  19. package/dist/assets/themes/neobrutalism/_theme.js +27 -0
  20. package/dist/assets/themes/neobrutalism/_theme.scss +33 -0
  21. package/dist/assets/themes/neobrutalism/css/base/_mixins.scss +46 -0
  22. package/dist/assets/themes/neobrutalism/css/base/_root.scss +80 -0
  23. package/dist/assets/themes/neobrutalism/css/base/_typography.scss +77 -0
  24. package/dist/assets/themes/neobrutalism/css/base/_utilities.scss +25 -0
  25. package/dist/assets/themes/neobrutalism/css/components/_buttons.scss +148 -0
  26. package/dist/assets/themes/neobrutalism/css/components/_cards.scss +69 -0
  27. package/dist/assets/themes/neobrutalism/css/components/_forms.scss +88 -0
  28. package/dist/assets/themes/neobrutalism/css/components/_infinite-scroll.scss +94 -0
  29. package/dist/assets/themes/neobrutalism/css/layout/_general.scss +200 -0
  30. package/dist/assets/themes/neobrutalism/css/layout/_navigation.scss +153 -0
  31. package/dist/assets/themes/neobrutalism/js/initialize-tooltips.js +20 -0
  32. package/dist/assets/themes/neobrutalism/js/navbar-scroll.js +29 -0
  33. package/dist/assets/themes/neobrutalism/pages/index.scss +227 -0
  34. package/dist/assets/themes/neobrutalism/pages/pricing/index.scss +267 -0
  35. package/dist/assets/themes/neobrutalism/pages/test/libraries/layers/index.js +9 -0
  36. package/dist/assets/themes/neobrutalism/pages/test/libraries/layers/index.scss +7 -0
  37. package/dist/build.js +2 -5
  38. package/dist/commands/install.js +1 -1
  39. package/dist/commands/setup.js +41 -0
  40. package/dist/defaults/CLAUDE.md +5 -1
  41. package/dist/defaults/dist/_includes/core/head.html +17 -0
  42. package/dist/defaults/dist/_includes/themes/classy/frontend/sections/footer.html +4 -4
  43. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +2 -0
  44. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/feedback.html +7 -3
  45. package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/core/base.html +31 -0
  46. package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/index.html +345 -0
  47. package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/pricing.html +483 -0
  48. package/dist/defaults/dist/pages/test/libraries/layers.html +57 -0
  49. package/dist/defaults/src/_config.yml +2 -0
  50. package/dist/defaults/test/_init.js +10 -0
  51. package/dist/gulp/tasks/defaults.js +8 -0
  52. package/dist/gulp/tasks/sass.js +43 -2
  53. package/dist/gulp/tasks/translation.js +11 -0
  54. package/dist/gulp/tasks/utils/manage-test-layers.js +97 -0
  55. package/dist/index.js +30 -4
  56. package/dist/test/runner.js +62 -0
  57. package/dist/test/suites/build/manager.test.js +11 -4
  58. package/dist/test/suites/build/mode-helpers.test.js +54 -2
  59. package/dist/utils/mode-helpers.js +65 -40
  60. package/docs/assets.md +6 -1
  61. package/docs/environment-detection.md +85 -0
  62. package/docs/test-framework.md +48 -3
  63. package/docs/themes.md +451 -0
  64. package/package.json +2 -1
  65. package/docs/cross-context-helpers.md +0 -75
@@ -0,0 +1,69 @@
1
+ // Neobrutalism Theme — Cards
2
+ // Cards are the canonical "box": surface + hard ink border + offset shadow.
3
+ // Restyles Bootstrap .card so feature grids, pricing tables, blog cards, and
4
+ // the account UI all pick it up automatically.
5
+
6
+ .card {
7
+ background: var(--nb-surface);
8
+ border-radius: 0;
9
+ @include nb-border();
10
+ @include nb-shadow(md);
11
+
12
+ .card-header {
13
+ background: transparent;
14
+ border-bottom: $nb-border-width solid var(--nb-border-color);
15
+ font-family: $nb-font-display;
16
+ font-weight: 800;
17
+ padding: 1.1rem 1.35rem;
18
+ }
19
+
20
+ .card-body {
21
+ padding: 1.35rem;
22
+ }
23
+
24
+ .card-footer {
25
+ background: transparent;
26
+ border-top: $nb-border-width solid var(--nb-border-color);
27
+ padding: 1rem 1.35rem;
28
+ }
29
+
30
+ // Card images sit flush against the top border
31
+ .card-img-top {
32
+ border-radius: 0;
33
+ border-bottom: $nb-border-width solid var(--nb-border-color);
34
+ }
35
+ }
36
+
37
+ // Clickable cards press like buttons
38
+ a.card,
39
+ .card.nb-pressable {
40
+ @include nb-press();
41
+ text-decoration: none;
42
+ }
43
+
44
+ // Emphasis card — thicker frame + bigger shadow (use for "featured"/popular tiers)
45
+ .card.nb-card-feature {
46
+ @include nb-border($nb-border-width-lg);
47
+ @include nb-shadow(lg);
48
+ }
49
+
50
+ // Accent-headed card: colored header strip
51
+ @each $name, $color in (
52
+ blue: $nb-accent-blue,
53
+ pink: $nb-accent-pink,
54
+ yellow: $nb-accent-yellow,
55
+ green: $nb-accent-green,
56
+ purple: $nb-accent-purple,
57
+ orange: $nb-accent-orange,
58
+ ) {
59
+ .card.nb-card-#{$name} > .card-header {
60
+ background: var(--nb-accent-#{$name});
61
+ color: #111111;
62
+ }
63
+ }
64
+
65
+ // List groups inside cards keep hard dividers
66
+ .list-group-item {
67
+ border-radius: 0;
68
+ border-color: var(--nb-border-color);
69
+ }
@@ -0,0 +1,88 @@
1
+ // Neobrutalism Theme — Forms
2
+ // Inputs are hard-bordered boxes. On focus they gain the offset shadow rather
3
+ // than a glow ring — consistent with the rest of the system. Powers auth pages
4
+ // (signin/signup), the contact form, and the hero demo form.
5
+
6
+ .form-control,
7
+ .form-select {
8
+ background-color: var(--nb-surface);
9
+ color: var(--bs-body-color);
10
+ border-radius: 0;
11
+ @include nb-border();
12
+ padding: 0.6rem 0.85rem;
13
+ font-weight: 500;
14
+ transition: $nb-transition;
15
+
16
+ &::placeholder {
17
+ color: rgba(var(--bs-body-color-rgb, 17, 17, 17), 0.45);
18
+ font-weight: 400;
19
+ }
20
+
21
+ &:focus {
22
+ background-color: var(--nb-surface);
23
+ color: var(--bs-body-color);
24
+ border-color: var(--nb-border-color);
25
+ @include nb-shadow(sm);
26
+ outline: 0;
27
+ }
28
+
29
+ &:disabled,
30
+ &[readonly] {
31
+ background-color: var(--nb-surface-alt);
32
+ opacity: 1;
33
+ }
34
+ }
35
+
36
+ // Labels: bold, slightly mono for the editorial feel
37
+ .form-label {
38
+ font-weight: 700;
39
+ margin-bottom: 0.4rem;
40
+ }
41
+
42
+ // Checkboxes / radios: square, hard-edged
43
+ .form-check-input {
44
+ border-radius: 0;
45
+ border: $nb-border-width-sm solid var(--nb-border-color);
46
+ background-color: var(--nb-surface);
47
+
48
+ &:checked {
49
+ background-color: $primary;
50
+ border-color: var(--nb-border-color);
51
+ }
52
+
53
+ &:focus {
54
+ border-color: var(--nb-border-color);
55
+ box-shadow: none;
56
+ }
57
+ }
58
+
59
+ // Switch keeps square track (intentionally un-rounded)
60
+ .form-switch .form-check-input {
61
+ border-radius: 0;
62
+ }
63
+
64
+ // Input groups: shared ink frame, no gaps
65
+ .input-group {
66
+ .form-control,
67
+ .form-select,
68
+ .btn,
69
+ .input-group-text {
70
+ border-radius: 0;
71
+ }
72
+
73
+ .input-group-text {
74
+ background: var(--nb-surface-alt);
75
+ @include nb-border();
76
+ font-weight: 700;
77
+ }
78
+ }
79
+
80
+ // Validation states use accent colors with the ink frame intact
81
+ .form-control.is-invalid,
82
+ .was-validated .form-control:invalid {
83
+ border-color: $danger;
84
+ }
85
+ .form-control.is-valid,
86
+ .was-validated .form-control:valid {
87
+ border-color: $success;
88
+ }
@@ -0,0 +1,94 @@
1
+ // Neobrutalism Theme — Infinite Scroll
2
+ // Structural marquee used by the shared layouts (trusted-by logo strip,
3
+ // testimonial scroll). Mostly layout/animation (theme-agnostic); cosmetic bits
4
+ // are tuned to the neobrutalist look (square cards, hard hover shadow).
5
+ // NOTE: this is structural behavior the inherited Classy layouts depend on —
6
+ // see docs/themes.md "Structural vs visual components".
7
+
8
+ .infinite-scroll-wrapper {
9
+ position: relative;
10
+ width: 100vw;
11
+ margin-left: calc(-50vw + 50%);
12
+ overflow: hidden;
13
+
14
+ &.infinite-scroll-fade-edges {
15
+ &::before,
16
+ &::after {
17
+ content: '';
18
+ position: absolute;
19
+ top: 0;
20
+ bottom: 0;
21
+ width: 100px;
22
+ z-index: 10;
23
+ pointer-events: none;
24
+ }
25
+ &::before { left: 0; background: linear-gradient(to right, var(--bs-body-bg), transparent); }
26
+ &::after { right: 0; background: linear-gradient(to left, var(--bs-body-bg), transparent); }
27
+ }
28
+ }
29
+
30
+ .infinite-scroll-track {
31
+ display: flex;
32
+ width: fit-content;
33
+ animation: infinite-scroll var(--infinite-scroll-duration, 30s) linear infinite;
34
+
35
+ &:hover { animation-play-state: paused; }
36
+
37
+ &.gap-sm { gap: 1rem; }
38
+ &.gap-md { gap: 1.5rem; }
39
+ &.gap-lg { gap: 2rem; }
40
+ }
41
+
42
+ .infinite-scroll-item { flex-shrink: 0; }
43
+
44
+ @keyframes infinite-scroll {
45
+ 0% { transform: translateX(0); }
46
+ 100% { transform: translateX(var(--infinite-scroll-distance, -50%)); }
47
+ }
48
+
49
+ // Logo items
50
+ .infinite-scroll-item--logo {
51
+ padding: 0 3rem;
52
+ display: flex;
53
+ align-items: center;
54
+ height: 60px;
55
+
56
+ img, svg {
57
+ height: 40px;
58
+ width: auto;
59
+ max-width: 150px;
60
+ transition: $nb-transition;
61
+ }
62
+ }
63
+
64
+ // Card items (testimonials) — square, ink frame + hard offset shadow
65
+ .infinite-scroll-item--card {
66
+ width: 320px;
67
+ padding: 0.5rem 0.5rem 0.5rem 0; // give the offset shadow room so it isn't clipped
68
+
69
+ .card {
70
+ height: 100%;
71
+ border-radius: 0;
72
+ @include nb-border();
73
+ @include nb-shadow(md);
74
+
75
+ &:hover {
76
+ @include nb-shadow(lg);
77
+ }
78
+ }
79
+ }
80
+
81
+ @media (max-width: 768px) {
82
+ .infinite-scroll-wrapper.infinite-scroll-fade-edges {
83
+ &::before,
84
+ &::after { width: 50px; }
85
+ }
86
+
87
+ .infinite-scroll-item--logo {
88
+ padding: 0 2rem;
89
+ height: 50px;
90
+ img, svg { height: 30px; }
91
+ }
92
+
93
+ .infinite-scroll-item--card { width: 280px; }
94
+ }
@@ -0,0 +1,200 @@
1
+ // Neobrutalism Theme — General Layout
2
+ // Section rhythm, the hero treatment, footer, badges, and overrides for the
3
+ // gradient/accent classes the shared (Classy-derived) layouts emit. The goal:
4
+ // the inherited homepage/pricing/auth markup renders as flat neobrutalist
5
+ // color-blocks instead of soft gradients — no layout override needed.
6
+
7
+ // ============================================
8
+ // Section rhythm
9
+ // ============================================
10
+ // Shared layouts rely on the theme to provide vertical section padding.
11
+ section {
12
+ padding-top: clamp(3rem, 7vw, 6rem);
13
+ padding-bottom: clamp(3rem, 7vw, 6rem);
14
+ }
15
+
16
+ // The navbar is fixed and overlaps the page, so the FIRST section on every page
17
+ // must add the navbar's height to its top padding — otherwise its content sits
18
+ // under the nav (cramped at the top, unbalanced vs. the section's bottom).
19
+ // Targets the first section regardless of page; the !important beats a `pt-0`
20
+ // utility that a layout may put on the first section.
21
+ main > section:first-child,
22
+ #main-content > section:first-child {
23
+ padding-top: calc(var(--nb-navbar-height) + clamp(2rem, 5vw, 4rem)) !important;
24
+ }
25
+
26
+ // ============================================
27
+ // Hero — flat color block, not a gradient
28
+ // ============================================
29
+ // The hero <section> ships with .bg-gradient-rainbow .gradient-animated etc.
30
+ // We neutralize the gradient and replace it with a solid accent block framed
31
+ // by a thick ink rule at the bottom. Decorative grain/animation are dropped.
32
+ .bg-gradient-rainbow,
33
+ .gradient-animated,
34
+ .gradient-grain {
35
+ background-image: none !important;
36
+ animation: none !important;
37
+ }
38
+
39
+ section.bg-gradient-rainbow {
40
+ background-color: var(--nb-accent-yellow) !important;
41
+ color: #111111 !important;
42
+ border-radius: 0 !important;
43
+ border-bottom: $nb-border-width-lg solid var(--nb-border-color);
44
+ box-shadow: none !important;
45
+
46
+ // Force readable ink text over the bright block
47
+ // (exclude the tagline kicker, which is ink-on-accent and styled below)
48
+ .text-light,
49
+ &.text-light,
50
+ h1, h2, h3, .lead,
51
+ p:not(.text-uppercase) {
52
+ color: #111111 !important;
53
+ }
54
+
55
+ // Tagline becomes a mono kicker chip: accent text on an ink block
56
+ .text-uppercase {
57
+ display: inline-block;
58
+ font-family: $nb-font-mono;
59
+ font-weight: 700;
60
+ background: #111111;
61
+ color: var(--nb-accent-yellow) !important;
62
+ padding: 0.4rem 0.8rem;
63
+ opacity: 1 !important;
64
+ }
65
+ }
66
+
67
+ // Accent text (headline_accent spans) — marker highlight instead of color swap
68
+ .text-accent {
69
+ background: #111111;
70
+ color: var(--nb-accent-yellow);
71
+ padding: 0 0.12em;
72
+ box-decoration-break: clone;
73
+ -webkit-box-decoration-break: clone;
74
+ }
75
+
76
+ // ============================================
77
+ // CTA / promo cards (.card.bg-gradient.bg-primary.text-white)
78
+ // ============================================
79
+ // Flatten gradient promo cards into solid accent blocks with ink frame.
80
+ .card.bg-gradient,
81
+ .card.bg-primary {
82
+ background-image: none !important;
83
+ background-color: var(--nb-accent-blue) !important;
84
+ color: #fff !important;
85
+ @include nb-border();
86
+ @include nb-shadow(lg);
87
+ }
88
+
89
+ // Glassy hero-demo card → solid surface box
90
+ .bg-glassy,
91
+ .card.bg-glassy {
92
+ background: var(--nb-surface) !important;
93
+ backdrop-filter: none !important;
94
+ -webkit-backdrop-filter: none !important;
95
+ @include nb-border();
96
+ }
97
+
98
+ // ============================================
99
+ // Shadow utility overrides → hard offset shadows
100
+ // ============================================
101
+ // Bootstrap's .shadow / .shadow-lg are soft blurs. Map them to the neobrutalist
102
+ // hard offset so inherited markup (auth cards, modals) gets the right look.
103
+ .shadow-sm { box-shadow: var(--nb-shadow-sm) !important; }
104
+ .shadow { box-shadow: var(--nb-shadow) !important; }
105
+ .shadow-lg { box-shadow: var(--nb-shadow-lg) !important; }
106
+
107
+ // Cards that ship with .border-0 (e.g. the auth card) lose Bootstrap's border;
108
+ // give the frame back so the ink outline + hard shadow read as one box.
109
+ // (Targeted to elevated cards so flat inline cards stay borderless.)
110
+ .card.border-0.shadow-sm,
111
+ .card.border-0.shadow,
112
+ .card.border-0.shadow-lg {
113
+ @include nb-border();
114
+ }
115
+
116
+ // ============================================
117
+ // Badges — sticker chips with ink frame
118
+ // ============================================
119
+ .badge {
120
+ border-radius: 0;
121
+ font-weight: 700;
122
+ letter-spacing: 0;
123
+ padding: 0.4rem 0.6rem;
124
+ @include nb-border($nb-border-width-sm);
125
+ color: #111111;
126
+ }
127
+ .badge.bg-body-tertiary { background: var(--nb-accent-yellow) !important; }
128
+ // Strip decorative gradient borders from inherited badges
129
+ .border-gradient-rainbow { border-image: none !important; }
130
+
131
+ // ============================================
132
+ // Footer
133
+ // ============================================
134
+ footer {
135
+ background: var(--nb-surface);
136
+ border-top: $nb-border-width-lg solid var(--nb-border-color);
137
+
138
+ a {
139
+ color: var(--bs-body-color);
140
+ text-decoration: none;
141
+ font-weight: 600;
142
+
143
+ &:hover {
144
+ background: var(--nb-accent-interactive);
145
+ color: var(--nb-accent-interactive-ink);
146
+ }
147
+ }
148
+ }
149
+
150
+ // ============================================
151
+ // Tables, alerts, accordions — square + framed
152
+ // ============================================
153
+ .alert {
154
+ border-radius: 0;
155
+ @include nb-border();
156
+ @include nb-shadow(sm);
157
+ }
158
+
159
+ .accordion-item {
160
+ border-radius: 0;
161
+ @include nb-border();
162
+ @include nb-shadow(sm);
163
+
164
+ // Bootstrap strips the top border on .accordion-item:not(:first-of-type) to
165
+ // avoid doubled borders in a connected stack. Our items are separated (mb-3),
166
+ // so force the full ink frame back on every item (!important beats Bootstrap's
167
+ // same-specificity reset that may load after this via bootstrap/overrides).
168
+ &:not(:first-of-type) {
169
+ border-top: $nb-border-width solid var(--nb-border-color) !important;
170
+ }
171
+ }
172
+ .accordion-button {
173
+ border-radius: 0;
174
+ font-weight: 700;
175
+
176
+ &:not(.collapsed) {
177
+ background: var(--nb-accent-yellow);
178
+ color: #111111;
179
+ box-shadow: none;
180
+ }
181
+ &:focus {
182
+ box-shadow: none;
183
+ border-color: var(--nb-border-color);
184
+ }
185
+ }
186
+
187
+ .modal-content,
188
+ .toast,
189
+ .offcanvas {
190
+ border-radius: 0;
191
+ @include nb-border();
192
+ @include nb-shadow(lg);
193
+ }
194
+
195
+ // Back-to-top / floating action buttons stay on-brand
196
+ .btn-back-to-top {
197
+ border-radius: 0;
198
+ @include nb-border();
199
+ @include nb-shadow(sm);
200
+ }
@@ -0,0 +1,153 @@
1
+ // Neobrutalism Theme — Navigation
2
+ // The inherited nav markup (.navbar-wrapper > .navbar.navbar-floating) is a
3
+ // floating glass pill in Classy. Here we make it a solid, ink-framed bar that
4
+ // sits flush at the top — a hard horizontal block, true to the style.
5
+
6
+ .navbar-wrapper {
7
+ position: fixed;
8
+ top: 0;
9
+ left: 0;
10
+ right: 0;
11
+ z-index: 1030;
12
+ pointer-events: none;
13
+
14
+ .navbar {
15
+ pointer-events: auto;
16
+ width: 100%;
17
+ max-width: 100%;
18
+ margin: 0;
19
+ padding-left: clamp(1rem, 4vw, 3rem);
20
+ padding-right: clamp(1rem, 4vw, 3rem);
21
+ }
22
+ }
23
+
24
+ .navbar.navbar-floating {
25
+ border-radius: 0;
26
+ padding: 0.75rem clamp(1rem, 4vw, 3rem);
27
+ background: var(--nb-surface) !important;
28
+ border: 0;
29
+ border-bottom: $nb-border-width-lg solid var(--nb-border-color);
30
+ transition: $nb-transition;
31
+
32
+ // Slight shadow only once scrolled (set by _theme.js)
33
+ &.scrolled {
34
+ @include nb-shadow(sm);
35
+ }
36
+
37
+ .container,
38
+ .container-fluid {
39
+ padding: 0;
40
+ }
41
+ }
42
+
43
+ // Brand: chunky display type, no fade
44
+ .navbar-brand {
45
+ font-family: $nb-font-display;
46
+ font-weight: 900;
47
+ font-size: 1.4rem;
48
+ letter-spacing: -0.03em;
49
+
50
+ .brand-logo { height: 32px; width: auto; margin-right: 0.5rem; }
51
+ }
52
+
53
+ // Nav links: square hover blocks. Hover/active fill is the PRIMARY blue (white
54
+ // text) — not the yellow accent — so the top-bar interactions match the brand's
55
+ // blue buttons instead of the yellow CTA accent.
56
+ .navbar-nav .nav-link {
57
+ font-weight: 700;
58
+ font-size: 0.95rem;
59
+ padding: 0.4rem 0.85rem;
60
+ margin: 0 0.15rem;
61
+ border-radius: 0;
62
+ color: var(--bs-body-color);
63
+ border: $nb-border-width-sm solid transparent;
64
+ transition: $nb-transition;
65
+
66
+ // Interactive accent (blue), not the yellow signature accent. Single token so
67
+ // every hover/active state across the theme stays consistent.
68
+ &:hover {
69
+ background: var(--nb-accent-interactive);
70
+ color: var(--nb-accent-interactive-ink);
71
+ border-color: var(--nb-border-color);
72
+ }
73
+
74
+ // Active = solid blue block with white text (matches hover, always readable).
75
+ // Use &.active.active + !important to beat Bootstrap's .text-body utility that
76
+ // the nav include adds to links (which otherwise forces color back to body).
77
+ &.active,
78
+ &.active.text-body {
79
+ background: var(--nb-accent-interactive) !important;
80
+ color: var(--nb-accent-interactive-ink) !important;
81
+ border-color: var(--nb-border-color);
82
+ }
83
+ }
84
+
85
+ // Mobile toggle: a hard square button
86
+ .navbar-toggler {
87
+ border-radius: 0;
88
+ padding: 0.3rem 0.55rem;
89
+ background: var(--nb-surface);
90
+ @include nb-border($nb-border-width-sm);
91
+ transition: $nb-transition;
92
+
93
+ &:hover { background: var(--nb-accent-interactive); }
94
+ &:focus { box-shadow: var(--nb-shadow-sm); }
95
+
96
+ .navbar-toggler-icon {
97
+ width: 1.25em;
98
+ height: 1.25em;
99
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2817,17,17,1%29' stroke-linecap='square' stroke-miterlimit='10' stroke-width='3' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
100
+ }
101
+
102
+ [data-bs-theme="dark"] & .navbar-toggler-icon {
103
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28245,245,245,1%29' stroke-linecap='square' stroke-miterlimit='10' stroke-width='3' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
104
+ }
105
+ }
106
+
107
+ // Mobile collapsed menu becomes a bordered drawer panel
108
+ @media (max-width: 991.98px) {
109
+ .navbar-collapse {
110
+ margin-top: 0.75rem;
111
+ padding: 0.75rem;
112
+ background: var(--nb-surface);
113
+ @include nb-border();
114
+ @include nb-shadow(md);
115
+ }
116
+ }
117
+
118
+ // ============================================
119
+ // Dropdown menus — hard bordered panels
120
+ // ============================================
121
+ .dropdown-menu {
122
+ background: var(--nb-surface);
123
+ border-radius: 0;
124
+ @include nb-border();
125
+ @include nb-shadow(md);
126
+ padding: 0.4rem;
127
+ margin-top: 0.6rem;
128
+
129
+ .dropdown-item {
130
+ border-radius: 0;
131
+ font-weight: 600;
132
+ padding: 0.55rem 0.85rem;
133
+ color: var(--bs-body-color);
134
+
135
+ &:hover,
136
+ &:focus {
137
+ background: var(--nb-accent-interactive);
138
+ color: var(--nb-accent-interactive-ink);
139
+ }
140
+
141
+ &.active,
142
+ &:active {
143
+ background: var(--bs-body-color);
144
+ color: var(--nb-paper);
145
+ }
146
+ }
147
+
148
+ .dropdown-divider {
149
+ border-top: $nb-border-width-sm solid var(--nb-border-color);
150
+ opacity: 1;
151
+ margin: 0.4rem 0;
152
+ }
153
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Initialize Bootstrap Tooltips
3
+ * Finds all elements with data-bs-toggle="tooltip" and initializes them
4
+ */
5
+ export default function initializeTooltips() {
6
+ const $tooltipTriggers = document.querySelectorAll('[data-bs-toggle="tooltip"]');
7
+
8
+ // If no tooltips found, exit early
9
+ if ($tooltipTriggers.length === 0) {
10
+ return;
11
+ }
12
+
13
+ // Log the number of tooltips being initialized
14
+ console.log(`Initializing ${$tooltipTriggers.length} tooltips`);
15
+
16
+ // Initialize each tooltip
17
+ $tooltipTriggers.forEach(($el) => {
18
+ new bootstrap.Tooltip($el);
19
+ });
20
+ }
@@ -0,0 +1,29 @@
1
+ // Navbar scroll effect for the Neobrutalism theme.
2
+ // Toggles a `.scrolled` class on the floating navbar once the page is scrolled
3
+ // past a threshold; the SCSS turns that into a hard offset shadow under the bar.
4
+ // Threshold is configurable via the data-nb-scroll-threshold attribute on the navbar.
5
+ export default function setupNavbarScroll() {
6
+ const navbar = document.querySelector('.navbar-floating');
7
+ if (!navbar) {
8
+ return;
9
+ }
10
+
11
+ const threshold = parseInt(navbar.dataset.nbScrollThreshold, 10) || 20;
12
+
13
+ let ticking = false;
14
+ function update() {
15
+ navbar.classList.toggle('scrolled', window.scrollY > threshold);
16
+ ticking = false;
17
+ }
18
+
19
+ function onScroll() {
20
+ if (!ticking) {
21
+ ticking = true;
22
+ window.requestAnimationFrame(update);
23
+ }
24
+ }
25
+
26
+ // Initial state + listener
27
+ update();
28
+ window.addEventListener('scroll', onScroll, { passive: true });
29
+ }