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.
- package/CHANGELOG.md +14 -0
- package/CLAUDE-ATTRIBUTION.md +215 -0
- package/CLAUDE.md +7 -6
- package/README.md +1 -0
- package/dist/assets/css/pages/test/libraries/layers/index.scss +28 -0
- package/dist/assets/js/modules/redirect.js +5 -4
- package/dist/assets/js/pages/download/index.js +1 -1
- package/dist/assets/js/pages/feedback/index.js +7 -1
- package/dist/assets/js/pages/test/libraries/layers/index.js +11 -0
- package/dist/assets/themes/_template/README.md +50 -0
- package/dist/assets/themes/_template/_config.scss +60 -0
- package/dist/assets/themes/_template/_theme.js +13 -4
- package/dist/assets/themes/_template/_theme.scss +16 -4
- package/dist/assets/themes/_template/css/base/_root.scss +19 -0
- package/dist/assets/themes/_template/css/components/_components.scss +23 -0
- package/dist/assets/themes/classy/README.md +18 -6
- package/dist/assets/themes/neobrutalism/README.md +98 -0
- package/dist/assets/themes/neobrutalism/_config.scss +139 -0
- package/dist/assets/themes/neobrutalism/_theme.js +27 -0
- package/dist/assets/themes/neobrutalism/_theme.scss +33 -0
- package/dist/assets/themes/neobrutalism/css/base/_mixins.scss +46 -0
- package/dist/assets/themes/neobrutalism/css/base/_root.scss +80 -0
- package/dist/assets/themes/neobrutalism/css/base/_typography.scss +77 -0
- package/dist/assets/themes/neobrutalism/css/base/_utilities.scss +25 -0
- package/dist/assets/themes/neobrutalism/css/components/_buttons.scss +148 -0
- package/dist/assets/themes/neobrutalism/css/components/_cards.scss +69 -0
- package/dist/assets/themes/neobrutalism/css/components/_forms.scss +88 -0
- package/dist/assets/themes/neobrutalism/css/components/_infinite-scroll.scss +94 -0
- package/dist/assets/themes/neobrutalism/css/layout/_general.scss +200 -0
- package/dist/assets/themes/neobrutalism/css/layout/_navigation.scss +153 -0
- package/dist/assets/themes/neobrutalism/js/initialize-tooltips.js +20 -0
- package/dist/assets/themes/neobrutalism/js/navbar-scroll.js +29 -0
- package/dist/assets/themes/neobrutalism/pages/index.scss +227 -0
- package/dist/assets/themes/neobrutalism/pages/pricing/index.scss +267 -0
- package/dist/assets/themes/neobrutalism/pages/test/libraries/layers/index.js +9 -0
- package/dist/assets/themes/neobrutalism/pages/test/libraries/layers/index.scss +7 -0
- package/dist/build.js +2 -5
- package/dist/commands/install.js +1 -1
- package/dist/commands/setup.js +41 -0
- package/dist/defaults/CLAUDE.md +5 -1
- package/dist/defaults/dist/_includes/core/head.html +17 -0
- package/dist/defaults/dist/_includes/themes/classy/frontend/sections/footer.html +4 -4
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +2 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/feedback.html +7 -3
- package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/core/base.html +31 -0
- package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/index.html +345 -0
- package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/pricing.html +483 -0
- package/dist/defaults/dist/pages/test/libraries/layers.html +57 -0
- package/dist/defaults/src/_config.yml +2 -0
- package/dist/defaults/test/_init.js +10 -0
- package/dist/gulp/tasks/defaults.js +8 -0
- package/dist/gulp/tasks/sass.js +43 -2
- package/dist/gulp/tasks/translation.js +11 -0
- package/dist/gulp/tasks/utils/manage-test-layers.js +97 -0
- package/dist/index.js +30 -4
- package/dist/test/runner.js +62 -0
- package/dist/test/suites/build/manager.test.js +11 -4
- package/dist/test/suites/build/mode-helpers.test.js +54 -2
- package/dist/utils/mode-helpers.js +65 -40
- package/docs/assets.md +6 -1
- package/docs/environment-detection.md +85 -0
- package/docs/test-framework.md +48 -3
- package/docs/themes.md +451 -0
- package/package.json +2 -1
- 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
|
+
}
|