The previous pass kept the dark base and the red icons, which did not read as the acampada. Now: - Light theme: white page, black ink and borders, black buttons with a hard neon offset shadow, neon for focus and the active nav item; h1/h2 in Anton with the stepped neon shadow of the site headings. - Nav, empty-state, spinner and pattern assets in black; platform icons keep their dark disc with a neon stroke. - New mark: three dome tents wired from their doors into a switch with an antenna, drawn in the inked style of the acampada logo (curved poles, ball ends, hatching) over a neon offset copy. LogoMark renders it inline; visible product name is T-HUB. - docs/brand: t-hub.svg and a lockup with the Anton wordmark converted to outlines so it renders without the font. - Public submission page moved to the light palette. Checked with headless Chromium screenshots (offline sample data) on desktop and mobile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
225 lines
7.4 KiB
CSS
225 lines
7.4 KiB
CSS
@import "tailwindcss";
|
||
|
||
/* Anton (SIL OFL 1.1, public/fonts/Anton-OFL.txt) — same title face as acampada26s.net. */
|
||
@font-face {
|
||
font-family: "Anton";
|
||
src: url("/fonts/anton.woff2") format("woff2");
|
||
font-display: swap;
|
||
}
|
||
|
||
@theme {
|
||
/* T-HUB: the acampada26s.net palette. White page, black ink and borders,
|
||
neon #00e5ff for hover, focus and the offset shadows. */
|
||
--color-bg: #fff;
|
||
--color-surface: #fff;
|
||
--color-surface-2: #f2f2f2;
|
||
--color-border: #000;
|
||
--color-border-subtle: #d4d4d4;
|
||
--color-text: #000;
|
||
--color-text-dim: #333;
|
||
--color-text-faint: #666;
|
||
--color-accent: #000;
|
||
--color-accent-hover: #222;
|
||
--color-accent-dim: rgb(0 229 255 / 0.28);
|
||
--color-neon: #00e5ff;
|
||
--color-live: #007a3d;
|
||
--color-warn: #8a5a00;
|
||
--color-danger: #c4001a;
|
||
--color-added: #007a3d;
|
||
--color-removed: #c4001a;
|
||
|
||
--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||
--font-mono: ui-monospace, "SF Mono", Menlo, monospace;
|
||
--font-title: "Anton", Impact, "Arial Narrow", sans-serif;
|
||
|
||
--radius-sm: 0.375rem;
|
||
--radius-md: 0.5rem;
|
||
--radius-lg: 0.75rem;
|
||
|
||
--text-2xs: 0.6875rem;
|
||
--text-xs: 0.75rem;
|
||
--text-sm: 0.8125rem;
|
||
--text-base: 0.875rem;
|
||
--text-lg: 1rem;
|
||
|
||
--mobile-tab-height: 76px;
|
||
--mobile-fab-size: 64px;
|
||
--mobile-sheet-radius: 28px;
|
||
--mobile-safe-bottom: env(safe-area-inset-bottom, 0px);
|
||
}
|
||
|
||
@theme inline {
|
||
--color-brand: var(--color-accent);
|
||
}
|
||
|
||
html {
|
||
color-scheme: light;
|
||
}
|
||
|
||
body {
|
||
background: var(--color-bg);
|
||
color: var(--color-text);
|
||
font-family: var(--font-sans);
|
||
font-size: var(--text-base);
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
html, body { max-width: 100%; }
|
||
|
||
/* Titles as on acampada26s.net: Anton, uppercase, hard neon offset. */
|
||
h1, h2 {
|
||
font-family: var(--font-title);
|
||
font-weight: 400;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.01em;
|
||
}
|
||
h1 {
|
||
font-size: 1.75rem;
|
||
line-height: 1.05;
|
||
text-shadow: 0.01em 0.01em 0 var(--color-neon), 0.02em 0.02em 0 var(--color-neon), 0.03em 0.03em 0 var(--color-neon), 0.04em 0.04em 0 var(--color-neon);
|
||
}
|
||
|
||
/* Buttons as on acampada26s.net: hard offset shadow, neon on hover. */
|
||
button.bg-accent, a.bg-accent {
|
||
box-shadow: 0 3px 0 var(--color-neon);
|
||
}
|
||
button.bg-accent:hover, a.bg-accent:hover, button.bg-accent:focus-visible, a.bg-accent:focus-visible {
|
||
box-shadow: 0 5px 0 var(--color-neon);
|
||
}
|
||
:focus-visible {
|
||
outline-color: var(--color-neon);
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
body { overflow-x: hidden; }
|
||
[data-mobile-main] :is(button, a[href], input, select, textarea, [role="tab"]) {
|
||
min-height: 44px;
|
||
}
|
||
[data-mobile-main] :is(button, a[href], [role="tab"]) {
|
||
min-width: 44px;
|
||
}
|
||
[data-mobile-main] p a[href] { display: inline-flex; align-items: center; }
|
||
[data-mobile-main] .mobile-stack-table,
|
||
[data-mobile-main] .mobile-stack-table tbody,
|
||
[data-mobile-main] .mobile-stack-table tr,
|
||
[data-mobile-main] .mobile-stack-table td { display: block; width: 100%; min-width: 0; }
|
||
[data-mobile-main] .mobile-stack-table thead { display: none; }
|
||
[data-mobile-main] .mobile-stack-table tr { padding: 8px 0; }
|
||
[data-mobile-main] .mobile-stack-table td { overflow-wrap: anywhere; }
|
||
[data-mobile-main] .mobile-stack-table td > * { max-width: 100%; }
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------
|
||
Phone tier (≤640px) — native feel, touch targets, safe areas, overflow.
|
||
Scoped globally (body-wide) rather than to [data-mobile-main] because the
|
||
auth screens (/login, /signup, /join, /submit, /oidc) and the body-portal
|
||
sheets render outside the main shell and were getting none of this.
|
||
These rules are unlayered, so they outrank Tailwind's `@layer utilities`
|
||
without needing !important.
|
||
--------------------------------------------------------------------------- */
|
||
@media (max-width: 640px) {
|
||
/* -- Native feel: no tap highlight flash, no double-tap-zoom delay on
|
||
controls (pinch zoom stays available — `manipulation` only disables
|
||
the double-tap gesture), no iOS landscape text inflation, no document
|
||
rubber-band behind the app shell. -- */
|
||
html {
|
||
-webkit-text-size-adjust: 100%;
|
||
text-size-adjust: 100%;
|
||
overscroll-behavior: none;
|
||
overflow-x: hidden;
|
||
}
|
||
body {
|
||
max-width: 100%;
|
||
overflow-x: hidden;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
body :where(button, a[href], [role="tab"], [role="button"], [role="switch"]) {
|
||
touch-action: manipulation;
|
||
}
|
||
|
||
/* -- ≥44px touch targets for buttons / icon-buttons (glyph buttons here are
|
||
plain <button>, bottom-nav tabs are <a>, segmented control is role=tab,
|
||
sheet drag handle is role=button). min-width is limited to controls that
|
||
are already box-shaped so inline text links keep their flow shape. -- */
|
||
body :where(button, a[href], [role="tab"], [role="button"], [role="switch"]) {
|
||
min-height: 44px;
|
||
}
|
||
body :where(nav button, nav a[href], [role="tab"]) {
|
||
min-width: 44px;
|
||
}
|
||
|
||
/* -- 16px minimum on text-entry controls: iOS Safari auto-zooms the viewport
|
||
on focus whenever the field's computed font-size is under 16px, which
|
||
reads as janky/non-native. Non-text inputs excluded (their size is
|
||
visual, not textual). -- */
|
||
body :where(
|
||
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="button"]):not([type="submit"]):not([type="reset"]),
|
||
select,
|
||
textarea
|
||
) {
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* -- Icon sizing. Icons size via width/height attributes from React props,
|
||
not a CSS variable or utility class — but HTML sizing attributes are
|
||
presentational hints, so author CSS still wins without touching TSX.
|
||
Bottom-nav glyph: 24px → 22px (target band 20–22px; top of band keeps
|
||
it clearest at phone density). Inline SVG icons: ≥16px floor so the
|
||
14px spinners and 11px arrow glyphs stop rendering sub-legibly. -- */
|
||
nav[aria-label="Primary navigation"] img {
|
||
width: 22px;
|
||
height: 22px;
|
||
}
|
||
body :where(svg) {
|
||
min-width: 16px;
|
||
min-height: 16px;
|
||
}
|
||
|
||
/* -- Safe areas. Bottom is already covered app-wide via --mobile-safe-bottom
|
||
(bottom nav, sheets, compose's fixed action bar). This adds the other
|
||
three edges on the shell column that holds top bar + main + bottom nav
|
||
— the notch/home-indicator sides that only bite in landscape.
|
||
max() keeps the value at 0 on devices without insets, and degrades to
|
||
no padding at all on browsers without :has(). -- */
|
||
:has(> main[data-mobile-main]) {
|
||
padding-top: max(0px, env(safe-area-inset-top, 0px));
|
||
padding-left: max(0px, env(safe-area-inset-left, 0px));
|
||
padding-right: max(0px, env(safe-area-inset-right, 0px));
|
||
}
|
||
|
||
/* -- Horizontal overflow guards: belt-and-braces on the scroller plus a
|
||
cap on replaced media, the classic cause of stray phone scroll. -- */
|
||
main[data-mobile-main] {
|
||
max-width: 100%;
|
||
}
|
||
body :where(img, video, canvas, iframe, svg) {
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
|
||
::selection {
|
||
background: var(--color-accent-dim);
|
||
}
|
||
|
||
:focus-visible {
|
||
outline: 2px solid var(--color-accent);
|
||
outline-offset: 1px;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
transition-duration: 0.01ms !important;
|
||
animation-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
|
||
.scrollbar-thin::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||
background: var(--color-border);
|
||
border-radius: 3px;
|
||
}
|