@gemboss/website-ui 0.6.0 → 0.8.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/AGENTS.md +25 -2
- package/CHANGELOG.md +130 -0
- package/README.md +8 -8
- package/bin/lib/check.mjs +70 -1
- package/css/base.css +12 -0
- package/css/footer.css +178 -0
- package/css/header.css +176 -0
- package/css/tokens.css +1 -0
- package/demo/footer.html +339 -0
- package/demo/header.html +356 -0
- package/demo/index.html +2 -0
- package/docs/components.md +125 -8
- package/docs/markup-contract.md +158 -6
- package/docs/theming.md +9 -2
- package/js/website.js +136 -5
- package/js/website.mjs +170 -5
- package/package.json +1 -1
- package/skill/SKILL.md +7 -0
- package/system.json +2 -0
- package/templates/footer.html +108 -20
- package/templates/header.html +34 -6
- package/templates/page.html +16 -11
- package/website.css +368 -1
- package/website.min.css +1 -1
package/website.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @gemboss/website-ui 0.
|
|
1
|
+
/*! @gemboss/website-ui 0.8.0 | generated by scripts/build.mjs from the files in css/. Edit those and not this file */
|
|
2
2
|
/* @gemboss/website-ui tokens. GENERATED from system.json by scripts/build.mjs: do not edit by hand. */
|
|
3
3
|
:root {
|
|
4
4
|
color-scheme: light;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
--gw-text-30: 30px;
|
|
17
17
|
--gw-display-h1: clamp(40px, 4.6vw, 68px);
|
|
18
18
|
--gw-display-h2: clamp(32px, 3.4vw, 52px);
|
|
19
|
+
--gw-display-wordmark: clamp(40px, 9vw, 128px);
|
|
19
20
|
--gw-display-weight: 600;
|
|
20
21
|
--gw-display-tracking: -0.03em;
|
|
21
22
|
--gw-heading-weight: 650;
|
|
@@ -337,6 +338,18 @@ h5, h6 { font-size: 16px; font-weight: 600; letter-spacing: 0; }
|
|
|
337
338
|
}
|
|
338
339
|
.gw-skip:focus { left: 16px; }
|
|
339
340
|
|
|
341
|
+
/* words for a screen reader only (0.7.0): " (opens in a new tab)" in a link with target="_blank" */
|
|
342
|
+
.gw-visually-hidden {
|
|
343
|
+
position: absolute;
|
|
344
|
+
width: 1px;
|
|
345
|
+
height: 1px;
|
|
346
|
+
padding: 0;
|
|
347
|
+
overflow: hidden;
|
|
348
|
+
clip-path: inset(50%);
|
|
349
|
+
white-space: nowrap;
|
|
350
|
+
border: 0;
|
|
351
|
+
}
|
|
352
|
+
|
|
340
353
|
/* scroll reveal: the script adds .is-in; the animation is a failsafe that shows it anyway after 2.5s */
|
|
341
354
|
.gw-reveal {
|
|
342
355
|
opacity: 0;
|
|
@@ -1026,6 +1039,182 @@ html.is-menu-open .gw-header { position: fixed; top: 0; left: 0; right: 0; }
|
|
|
1026
1039
|
.gw-header--float { background: var(--gw-surface); }
|
|
1027
1040
|
}
|
|
1028
1041
|
|
|
1042
|
+
/* ---- 0.8.0: the drill-down mobile menu (gap #41) and the language switcher (gap #29) --------------------------------
|
|
1043
|
+
Every rule below selects a part 0.7.0 does not have (data-gw-menu="drill", the script's .is-drill, the menu's
|
|
1044
|
+
__head, __back, __title, __heading and __icon, .gw-lang), so a 0.7.0 header looks and works exactly as before. */
|
|
1045
|
+
|
|
1046
|
+
/* The drill-down menu, as gemboss.ai's phone menu (v4, approved 15/10/2026), opt-in on the sheet:
|
|
1047
|
+
<div class="gw-mobile-menu" data-gw-menu="drill">. Its look needs only the attribute: top-level rows of 64px with
|
|
1048
|
+
24px words and one hairline each, a level's links with an optional icon and line, and the actions in a bar that
|
|
1049
|
+
stays at the bottom of the sheet while the list scrolls (the sheet has no bottom padding, so nothing peeks out under
|
|
1050
|
+
the bar; nor sideways while a level slides in). Without the script the groups are the 0.7.0 <details> that open in
|
|
1051
|
+
place, their chevron pointing down. */
|
|
1052
|
+
.gw-mobile-menu[data-gw-menu="drill"] { display: flex; flex-direction: column; overflow-x: hidden; padding-bottom: 0; }
|
|
1053
|
+
.gw-mobile-menu[data-gw-menu="drill"] > nav { display: flex; flex: 1 0 auto; flex-direction: column; }
|
|
1054
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__group > summary,
|
|
1055
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__top { min-height: 64px; font-size: 24px; letter-spacing: -0.01em; }
|
|
1056
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__group > summary::after { width: 20px; height: 20px; -webkit-mask-size: 20px; mask-size: 20px; }
|
|
1057
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__list { padding: 0; }
|
|
1058
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__list .gw-mobile-menu__link {
|
|
1059
|
+
gap: 12px;
|
|
1060
|
+
min-height: 56px;
|
|
1061
|
+
padding-block: 12px;
|
|
1062
|
+
border-bottom: 1px solid var(--gw-line);
|
|
1063
|
+
color: var(--gw-ink);
|
|
1064
|
+
font-weight: 500;
|
|
1065
|
+
}
|
|
1066
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__link b { display: block; font-size: 16px; font-weight: 500; line-height: 20px; }
|
|
1067
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__link i { display: block; margin-top: 2px; color: var(--gw-muted); font-size: 13px; font-style: normal; line-height: 18px; }
|
|
1068
|
+
/* a level's optional parts: a muted heading over a run of links, a 32px icon tile before a link's words */
|
|
1069
|
+
.gw-mobile-menu__heading { padding: 16px 0 8px; color: var(--gw-muted); font-size: 12px; font-weight: 500; line-height: 1.5; }
|
|
1070
|
+
.gw-mobile-menu__icon {
|
|
1071
|
+
display: grid;
|
|
1072
|
+
place-items: center;
|
|
1073
|
+
flex: none;
|
|
1074
|
+
width: 32px;
|
|
1075
|
+
height: 32px;
|
|
1076
|
+
border: 1px solid var(--gw-line);
|
|
1077
|
+
border-radius: var(--gw-radius-action);
|
|
1078
|
+
color: var(--gw-muted);
|
|
1079
|
+
}
|
|
1080
|
+
.gw-mobile-menu__icon svg { width: 16px; height: 16px; }
|
|
1081
|
+
/* a level's last link, to the section's own page: its row opens the level instead of going there */
|
|
1082
|
+
.gw-mobile-menu__all { display: flex; align-items: center; gap: 6px; min-height: 56px; color: var(--gw-accent-strong); font-size: 16px; font-weight: 500; }
|
|
1083
|
+
.gw-mobile-menu__all::after { content: "\2192"; }
|
|
1084
|
+
/* the action bar: what is not a button (Sign in, the language) on the first line, the last one on the right; the
|
|
1085
|
+
buttons side by side under it (gw-btn--lg: 48px, as gemboss.ai) */
|
|
1086
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions {
|
|
1087
|
+
position: sticky;
|
|
1088
|
+
bottom: 0;
|
|
1089
|
+
z-index: 1;
|
|
1090
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
1091
|
+
align-items: center;
|
|
1092
|
+
gap: 8px 16px;
|
|
1093
|
+
margin-top: auto;
|
|
1094
|
+
padding-block: 12px calc(12px + env(safe-area-inset-bottom));
|
|
1095
|
+
border-top: 1px solid var(--gw-line);
|
|
1096
|
+
background: var(--gw-bg);
|
|
1097
|
+
}
|
|
1098
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > .gw-btn { grid-row: 2; }
|
|
1099
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > :not(.gw-btn) { grid-row: 1; }
|
|
1100
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > :not(.gw-btn):last-child { grid-column: 2; justify-self: end; }
|
|
1101
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > .gw-mobile-menu__link { color: var(--gw-ink); font-weight: 500; }
|
|
1102
|
+
|
|
1103
|
+
/* Wired (.is-drill, set by website.js): a group's row points right and opens its level, which shows alone
|
|
1104
|
+
(.is-drilled) under its head: Back on the left, the level's title in the middle. The sheet starts where the header
|
|
1105
|
+
ends (--gw-menu-top, measured by the script) and the header stays in place, so an announcement bar above it does not
|
|
1106
|
+
jump away. */
|
|
1107
|
+
.gw-mobile-menu.is-drill { top: var(--gw-menu-top, calc(var(--gw-header-height) + 1px)); }
|
|
1108
|
+
html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky; }
|
|
1109
|
+
.gw-mobile-menu.is-drill .gw-mobile-menu__group > summary::after { transform: rotate(-90deg); transition: none; }
|
|
1110
|
+
.gw-mobile-menu.is-drilled > nav > :not([open], .gw-mobile-menu__actions),
|
|
1111
|
+
.gw-mobile-menu.is-drilled .gw-mobile-menu__group[open] > summary { display: none; }
|
|
1112
|
+
.gw-mobile-menu__head { display: none; }
|
|
1113
|
+
.gw-mobile-menu.is-drilled .gw-mobile-menu__group[open] > .gw-mobile-menu__head {
|
|
1114
|
+
display: grid;
|
|
1115
|
+
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
1116
|
+
align-items: center;
|
|
1117
|
+
gap: 8px;
|
|
1118
|
+
min-height: 56px;
|
|
1119
|
+
margin-bottom: 8px;
|
|
1120
|
+
border-bottom: 1px solid var(--gw-line);
|
|
1121
|
+
}
|
|
1122
|
+
.gw-mobile-menu__back {
|
|
1123
|
+
display: inline-flex;
|
|
1124
|
+
align-items: center;
|
|
1125
|
+
justify-self: start;
|
|
1126
|
+
gap: 4px;
|
|
1127
|
+
min-height: var(--gw-tap-min);
|
|
1128
|
+
padding: 0 8px 0 0;
|
|
1129
|
+
border: 0;
|
|
1130
|
+
background: transparent;
|
|
1131
|
+
color: var(--gw-muted);
|
|
1132
|
+
font-size: 16px;
|
|
1133
|
+
line-height: 1.5;
|
|
1134
|
+
}
|
|
1135
|
+
.gw-mobile-menu__back:hover { color: var(--gw-ink); }
|
|
1136
|
+
.gw-mobile-menu__back::before {
|
|
1137
|
+
content: "";
|
|
1138
|
+
flex: none;
|
|
1139
|
+
width: 16px;
|
|
1140
|
+
height: 16px;
|
|
1141
|
+
background: currentColor;
|
|
1142
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M15 18l-6-6 6-6' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
|
|
1143
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M15 18l-6-6 6-6' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
|
|
1144
|
+
}
|
|
1145
|
+
.gw-mobile-menu__title { color: var(--gw-ink); font-size: 16px; font-weight: 600; letter-spacing: 0; line-height: 1.5; text-align: center; }
|
|
1146
|
+
/* a level slides in from the right, the top level back in from the left; under reduced motion they just switch */
|
|
1147
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1148
|
+
.gw-mobile-menu.is-drilled .gw-mobile-menu__group[open] { animation: gw-drill-in 0.24s var(--gw-ease); }
|
|
1149
|
+
.gw-mobile-menu.is-back > nav > :not(.gw-mobile-menu__actions) { animation: gw-drill-back 0.24s var(--gw-ease); }
|
|
1150
|
+
}
|
|
1151
|
+
@keyframes gw-drill-in { from { opacity: 0; transform: translateX(32px); } }
|
|
1152
|
+
@keyframes gw-drill-back { from { opacity: 0; transform: translateX(-32px); } }
|
|
1153
|
+
|
|
1154
|
+
/* The language switcher: <div class="gw-lang" data-gw-lang hidden>, a ghost button with the globe and the current
|
|
1155
|
+
language's short code, and its list of the other languages, a small popover card (the card radius of the site's
|
|
1156
|
+
profile, one hairline, the raised shadow). website.js fills the list from the page's <link rel="alternate" hreflang>
|
|
1157
|
+
and takes `hidden` away when there is another language. In the header's actions it shows from 1080px, where the nav
|
|
1158
|
+
is; below that a second one sits in the mobile menu's action bar, and its list opens upward. */
|
|
1159
|
+
.gw-lang { position: relative; flex: none; }
|
|
1160
|
+
.gw-header__actions > .gw-lang { display: none; }
|
|
1161
|
+
.gw-lang__btn {
|
|
1162
|
+
display: inline-flex;
|
|
1163
|
+
align-items: center;
|
|
1164
|
+
gap: 6px;
|
|
1165
|
+
height: 40px;
|
|
1166
|
+
padding: 0 12px;
|
|
1167
|
+
border: 0;
|
|
1168
|
+
border-radius: var(--gw-radius-action);
|
|
1169
|
+
background: transparent;
|
|
1170
|
+
color: var(--gw-ink-2);
|
|
1171
|
+
font-size: 14px;
|
|
1172
|
+
font-weight: 500;
|
|
1173
|
+
line-height: 1.4;
|
|
1174
|
+
white-space: nowrap;
|
|
1175
|
+
}
|
|
1176
|
+
.gw-lang__btn:hover,
|
|
1177
|
+
.gw-lang__btn[aria-expanded="true"] { background: var(--gw-bg-soft); color: var(--gw-ink); }
|
|
1178
|
+
.gw-lang__btn svg { flex: none; width: 18px; height: 18px; }
|
|
1179
|
+
.gw-lang__menu {
|
|
1180
|
+
position: absolute;
|
|
1181
|
+
top: calc(100% + 8px);
|
|
1182
|
+
right: 0;
|
|
1183
|
+
z-index: 1;
|
|
1184
|
+
display: grid;
|
|
1185
|
+
gap: 2px;
|
|
1186
|
+
min-width: 200px;
|
|
1187
|
+
margin: 0;
|
|
1188
|
+
padding: 8px;
|
|
1189
|
+
list-style: none;
|
|
1190
|
+
border: 1px solid var(--gw-line);
|
|
1191
|
+
border-radius: var(--gw-radius-card);
|
|
1192
|
+
background: var(--gw-surface);
|
|
1193
|
+
box-shadow: var(--gw-shadow-raised);
|
|
1194
|
+
}
|
|
1195
|
+
.gw-lang__link {
|
|
1196
|
+
display: flex;
|
|
1197
|
+
align-items: center;
|
|
1198
|
+
min-height: 40px;
|
|
1199
|
+
padding: 0 12px;
|
|
1200
|
+
border-radius: var(--gw-radius-action);
|
|
1201
|
+
color: var(--gw-ink);
|
|
1202
|
+
font-size: 14px;
|
|
1203
|
+
font-weight: 500;
|
|
1204
|
+
white-space: nowrap;
|
|
1205
|
+
}
|
|
1206
|
+
.gw-lang__link:hover { background: var(--gw-bg-soft); }
|
|
1207
|
+
.gw-mobile-menu .gw-lang__btn { padding: 0 8px 0 0; color: var(--gw-muted); font-size: 16px; }
|
|
1208
|
+
.gw-mobile-menu .gw-lang__btn:hover,
|
|
1209
|
+
.gw-mobile-menu .gw-lang__btn[aria-expanded="true"] { background: transparent; color: var(--gw-ink); }
|
|
1210
|
+
.gw-mobile-menu .gw-lang__menu { top: auto; right: auto; bottom: calc(100% + 8px); left: 0; }
|
|
1211
|
+
@media (max-width: 768px), (pointer: coarse) {
|
|
1212
|
+
.gw-lang__btn, .gw-lang__link { min-height: var(--gw-tap-min); }
|
|
1213
|
+
}
|
|
1214
|
+
@media (min-width: 1080px) {
|
|
1215
|
+
.gw-header__actions > .gw-lang { display: block; }
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1029
1218
|
/* footer: brand block, link columns, bottom row. One hairline on top, white like the page. */
|
|
1030
1219
|
.gw-footer {
|
|
1031
1220
|
padding-block: 64px 40px;
|
|
@@ -1083,6 +1272,184 @@ html.is-menu-open .gw-header { position: fixed; top: 0; left: 0; right: 0; }
|
|
|
1083
1272
|
.gw-footer__bottom a { min-height: var(--gw-tap-min); }
|
|
1084
1273
|
}
|
|
1085
1274
|
|
|
1275
|
+
/* ---- 0.7.0: the footer as gemboss.ai has it (issue #71) ---------------------------------------------------------------
|
|
1276
|
+
Every part below is optional, per site, and styles only its own markup: a 0.6.0 footer looks exactly as before.
|
|
1277
|
+
In page order: the Ask AI row in the brand block, columns that fold on phones (gap #40), product-family rows, the
|
|
1278
|
+
theme toggle in the bottom row, and the wordmark at the very end. Values measured on gemboss.ai on 25/10/2026 and
|
|
1279
|
+
put on the kit's scales; the full markup is templates/footer.html. */
|
|
1280
|
+
|
|
1281
|
+
/* Ask AI: a label and links that open an assistant with one neutral question already typed (a URL-encoded ?q=), so
|
|
1282
|
+
the answer comes from what the web says, not from a claim the brand wrote. Words, not logos. 28px rows, 44px on
|
|
1283
|
+
touch. */
|
|
1284
|
+
.gw-footer__ask { margin-top: 24px; }
|
|
1285
|
+
/* the row may run wider than the brand's sentence (four names on one line at 390px) */
|
|
1286
|
+
.gw-footer__brand:has(> .gw-footer__ask) { max-width: none; }
|
|
1287
|
+
.gw-footer__brand:has(> .gw-footer__ask) > :not(.gw-footer__ask) { max-width: 320px; }
|
|
1288
|
+
.gw-footer__ask > .gw-footer__ask-label { margin: 0; color: var(--gw-muted); font-size: 13px; }
|
|
1289
|
+
.gw-footer__ask > ul { display: flex; flex-wrap: wrap; gap: 0 16px; margin: 4px 0 0; padding: 0; list-style: none; }
|
|
1290
|
+
.gw-footer__ask a { display: block; padding-block: 4px; color: var(--gw-ink); line-height: 20px; }
|
|
1291
|
+
.gw-footer__ask a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
|
|
1292
|
+
|
|
1293
|
+
/* the mark after a link that leaves the site (↗, drawn), aria-hidden; the words "(opens in a new tab)" are a separate
|
|
1294
|
+
.gw-visually-hidden span, only when the link has target="_blank" */
|
|
1295
|
+
.gw-footer__ext {
|
|
1296
|
+
display: inline-block;
|
|
1297
|
+
width: 12px;
|
|
1298
|
+
height: 12px;
|
|
1299
|
+
margin-left: 4px;
|
|
1300
|
+
vertical-align: -1px;
|
|
1301
|
+
background: var(--gw-muted);
|
|
1302
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.5 8.5l5-5M4.5 3.5h4v4' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
|
|
1303
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.5 8.5l5-5M4.5 3.5h4v4' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/* Folds: <details class="gw-footer__fold"> inside a .gw-footer__col (or a .gw-footer__family), its <summary> holding the
|
|
1307
|
+
.gw-footer__heading, then the <ul>. Below 768px each is a 52px row with a chevron, folded unless the markup says
|
|
1308
|
+
`open`; the browser opens and closes it, no script needed. From 768px it is the column as before: the summary is
|
|
1309
|
+
only the heading (display: contents: no box, no Tab stop), and the list shows even while the <details> is closed
|
|
1310
|
+
(::details-content; website.js also sets `open` there, for browsers without it). */
|
|
1311
|
+
.gw-footer__fold > summary { list-style: none; }
|
|
1312
|
+
.gw-footer__fold > summary::-webkit-details-marker { display: none; }
|
|
1313
|
+
.gw-footer__fold .gw-footer__heading { margin: 0; line-height: 20px; }
|
|
1314
|
+
.gw-footer__fold > ul { margin: 0; padding: 0; list-style: none; }
|
|
1315
|
+
.gw-footer__col > .gw-footer__fold a { line-height: 20px; }
|
|
1316
|
+
@media (min-width: 768px) {
|
|
1317
|
+
.gw-footer__fold > summary { display: contents; }
|
|
1318
|
+
.gw-footer__col > .gw-footer__fold .gw-footer__heading { margin-bottom: 12px; }
|
|
1319
|
+
}
|
|
1320
|
+
@media (min-width: 768px) {
|
|
1321
|
+
.gw-footer__fold::details-content { content-visibility: visible; }
|
|
1322
|
+
}
|
|
1323
|
+
@media (max-width: 767px) {
|
|
1324
|
+
.gw-footer__grid:has(> .gw-footer__col > .gw-footer__fold) { grid-template-columns: minmax(0, 1fr); row-gap: 0; }
|
|
1325
|
+
.gw-footer__grid:has(> .gw-footer__col > .gw-footer__fold) > .gw-footer__brand { padding-bottom: 24px; }
|
|
1326
|
+
.gw-footer__col:has(> .gw-footer__fold),
|
|
1327
|
+
.gw-footer__family { border-bottom: 1px solid var(--gw-line); }
|
|
1328
|
+
.gw-footer__brand + .gw-footer__col:has(> .gw-footer__fold) { border-top: 1px solid var(--gw-line); }
|
|
1329
|
+
.gw-footer__fold > summary {
|
|
1330
|
+
display: flex;
|
|
1331
|
+
align-items: center;
|
|
1332
|
+
justify-content: space-between;
|
|
1333
|
+
gap: 16px;
|
|
1334
|
+
min-height: var(--gw-tap-min);
|
|
1335
|
+
padding-block: 16px;
|
|
1336
|
+
cursor: pointer;
|
|
1337
|
+
}
|
|
1338
|
+
.gw-footer__fold > summary::after {
|
|
1339
|
+
content: "";
|
|
1340
|
+
flex: none;
|
|
1341
|
+
width: 16px;
|
|
1342
|
+
height: 16px;
|
|
1343
|
+
background: var(--gw-muted);
|
|
1344
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.5 5.5L8 11l5.5-5.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
|
|
1345
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.5 5.5L8 11l5.5-5.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
|
|
1346
|
+
transition: transform 0.18s var(--gw-ease);
|
|
1347
|
+
}
|
|
1348
|
+
.gw-footer__fold[open] > summary::after { transform: rotate(180deg); }
|
|
1349
|
+
.gw-footer__fold > ul { padding-bottom: 12px; }
|
|
1350
|
+
.gw-footer__col > .gw-footer__fold a { display: flex; align-items: center; min-height: var(--gw-tap-min); }
|
|
1351
|
+
}
|
|
1352
|
+
/* desktop: the brand's track is as wide as gemboss.ai draws it beside five columns (1.2 parts of 6.2, after five 24px
|
|
1353
|
+
gaps), whatever the number of columns, so a family row can line up with it; the columns share the rest */
|
|
1354
|
+
@media (min-width: 1080px) {
|
|
1355
|
+
.gw-footer__grid:has(> .gw-footer__col > .gw-footer__fold) { grid-template-columns: minmax(0, calc((100% - 120px) * 6 / 31)); }
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/* Product family: a labelled row of products, name and one line each (<b> and <i>), as many rows as the brand needs
|
|
1359
|
+
("More from GemBoss", "From the same team, on the Shopify App Store"). A <nav aria-label> holding a fold whose
|
|
1360
|
+
heading is the label. Two items a row on phones, four from 768px; from 1080px the label sits in the brand's track and
|
|
1361
|
+
the items start where the first column starts. */
|
|
1362
|
+
.gw-footer__family .gw-footer__heading { color: var(--gw-muted); font-weight: 500; }
|
|
1363
|
+
.gw-footer__family .gw-footer__fold > ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
1364
|
+
.gw-footer__family a {
|
|
1365
|
+
display: flex;
|
|
1366
|
+
flex-direction: column;
|
|
1367
|
+
justify-content: center;
|
|
1368
|
+
gap: 2px;
|
|
1369
|
+
min-height: var(--gw-tap-min);
|
|
1370
|
+
padding: 8px 12px 8px 0;
|
|
1371
|
+
border-radius: var(--gw-radius-action);
|
|
1372
|
+
color: var(--gw-ink);
|
|
1373
|
+
}
|
|
1374
|
+
.gw-footer__family a:hover { background: var(--gw-bg-soft); }
|
|
1375
|
+
.gw-footer__family b { font-size: 14px; font-weight: 600; line-height: 20px; }
|
|
1376
|
+
.gw-footer__family i { color: var(--gw-muted); font-size: 13px; font-style: normal; line-height: 18px; }
|
|
1377
|
+
@media (min-width: 768px) {
|
|
1378
|
+
.gw-footer__family { margin-top: 48px; padding-top: 32px; border-top: 1px solid var(--gw-line); }
|
|
1379
|
+
.gw-footer__family .gw-footer__heading { margin-bottom: 12px; }
|
|
1380
|
+
.gw-footer__family .gw-footer__fold > ul { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px 16px; margin-left: -12px; }
|
|
1381
|
+
.gw-footer__family a { min-height: 0; padding: 8px 12px; }
|
|
1382
|
+
.gw-footer__family + .gw-footer__bottom { margin-top: 32px; }
|
|
1383
|
+
}
|
|
1384
|
+
@media (min-width: 1080px) {
|
|
1385
|
+
.gw-footer__family { display: flow-root; }
|
|
1386
|
+
.gw-footer__family .gw-footer__heading { float: left; width: calc((100% - 120px) * 6 / 31); margin: 8px 0 0; }
|
|
1387
|
+
.gw-footer__family .gw-footer__fold > ul { margin-left: calc((100% - 120px) * 6 / 31 + 12px); }
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/* The theme toggle in the bottom row (.gw-theme-toggle with data-gw-theme-toggle, wired like the header's): a 32px
|
|
1391
|
+
square with one hairline, its hit area 40px, 44px on touch. It pushes itself and the social links to the right.
|
|
1392
|
+
Below 768px the copyright and the toggle share the first line, legal and social links the second. */
|
|
1393
|
+
.gw-footer__toggle {
|
|
1394
|
+
position: relative;
|
|
1395
|
+
width: 32px;
|
|
1396
|
+
height: 32px;
|
|
1397
|
+
margin-inline: auto 16px;
|
|
1398
|
+
border: 1px solid var(--gw-line-strong);
|
|
1399
|
+
background: var(--gw-surface);
|
|
1400
|
+
}
|
|
1401
|
+
.gw-footer__toggle::after { content: ""; position: absolute; inset: -5px; }
|
|
1402
|
+
.gw-footer__toggle svg { width: 18px; height: 18px; }
|
|
1403
|
+
@media (max-width: 768px), (pointer: coarse) {
|
|
1404
|
+
.gw-footer__toggle { width: var(--gw-tap-min); height: var(--gw-tap-min); }
|
|
1405
|
+
}
|
|
1406
|
+
@media (max-width: 767px) {
|
|
1407
|
+
.gw-footer__bottom:has(> .gw-footer__toggle) { column-gap: 16px; }
|
|
1408
|
+
.gw-footer__bottom:has(> .gw-footer__toggle) > :first-child { flex: 1 1 calc(100% - var(--gw-tap-min) - 16px); }
|
|
1409
|
+
.gw-footer__bottom > .gw-footer__toggle { order: 1; margin: 0; }
|
|
1410
|
+
.gw-footer__bottom:has(> .gw-footer__toggle) > ul { order: 2; }
|
|
1411
|
+
.gw-footer__family + .gw-footer__bottom,
|
|
1412
|
+
.gw-footer__grid:has(> .gw-footer__col > .gw-footer__fold) + .gw-footer__bottom { margin-top: 24px; }
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/* The wordmark: the brand's mark and name, large and faint, the footer's last line. Decoration (aria-hidden="true"):
|
|
1416
|
+
the name is already in the brand block. Its size follows the viewport (--gw-display-wordmark) and it never makes the
|
|
1417
|
+
page scroll sideways: a name too long for the line is cut. The mark is drawn in currentColor (an inline SVG); an
|
|
1418
|
+
<img> mark keeps its own colours, so leave it out and keep the name. Not on phones, as on gemboss.ai. 24px more room
|
|
1419
|
+
under it than gemboss.ai gives: the kit's footer is a painted band (white like the page), and the breathing gate
|
|
1420
|
+
wants half the letters' size between them and its edge (67px under 123px letters at 1366px). */
|
|
1421
|
+
.gw-footer__wordmark {
|
|
1422
|
+
display: flex;
|
|
1423
|
+
align-items: center;
|
|
1424
|
+
justify-content: center;
|
|
1425
|
+
gap: 24px;
|
|
1426
|
+
padding-block: 48px 24px;
|
|
1427
|
+
color: color-mix(in srgb, var(--gw-ink) 10%, transparent);
|
|
1428
|
+
font-size: var(--gw-display-wordmark);
|
|
1429
|
+
font-weight: 700;
|
|
1430
|
+
letter-spacing: var(--gw-display-tracking);
|
|
1431
|
+
line-height: 1;
|
|
1432
|
+
white-space: nowrap;
|
|
1433
|
+
user-select: none;
|
|
1434
|
+
}
|
|
1435
|
+
.gw-footer__wordmark > svg,
|
|
1436
|
+
.gw-footer__wordmark > img { flex: none; width: auto; height: 0.9em; }
|
|
1437
|
+
.gw-footer__wordmark > span { min-width: 0; overflow-x: clip; }
|
|
1438
|
+
@media (max-width: 767px) { .gw-footer__wordmark { gap: 16px; } }
|
|
1439
|
+
@media (max-width: 479px) { .gw-footer__wordmark { display: none; } }
|
|
1440
|
+
|
|
1441
|
+
@media (max-width: 768px), (pointer: coarse) {
|
|
1442
|
+
.gw-footer__ask a { display: flex; align-items: center; min-height: var(--gw-tap-min); padding-block: 0; }
|
|
1443
|
+
}
|
|
1444
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1445
|
+
.gw-footer__fold > summary::after { transition: none; }
|
|
1446
|
+
}
|
|
1447
|
+
/* a forced-colours screen drops backgrounds: draw the chevron and the ↗ in its text colour */
|
|
1448
|
+
@media (forced-colors: active) {
|
|
1449
|
+
.gw-footer__ext,
|
|
1450
|
+
.gw-footer__fold > summary::after { forced-color-adjust: none; background: CanvasText; }
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1086
1453
|
/* sections: hero, logos, stats, features, split, showcase, steps, quote, pricing, CTA. */
|
|
1087
1454
|
|
|
1088
1455
|
/* hero: the first screen. __wash is the one place the wash hues may appear. */
|