7fe09ea35e
- Add @font-face for JetBrainsMono Nerd Font Mono (nerd-fonts v3.3.0) with proper box-drawing glyph support for seamless Unicode diagram rendering - Update --ll-font-code to use NF Mono as primary, fallback to JetBrains Mono/SF Mono - Fix liveline-prism.css: use var(--ll-font-code) instead of hardcoded SF Mono - Remove unused Google Fonts JetBrains Mono link from root.tsx - Fix diagram: keep Unicode box chars for seamless lines, use ASCII < > v for arrows (◄ ► ▼ render at inconsistent widths), recount all columns
292 lines
9.6 KiB
CSS
292 lines
9.6 KiB
CSS
/*
|
|
* Liveline page styles - recreating benji.org's editorial design system.
|
|
*
|
|
* Key design decisions from the original:
|
|
* - Inter variable font at weight 460 (between normal and medium)
|
|
* - Near-black text (#111) not pure black, with 40% opacity for secondary
|
|
* - Narrow 550px content column for optimal reading
|
|
* - Negative letter-spacing on headings for tight, refined feel
|
|
* - 7-layer box shadows for realistic depth
|
|
* - Display P3 accent colors (wider gamut than sRGB)
|
|
* - Staggered entrance animations with subtle translateY
|
|
* - Custom cubic-bezier easings with slight overshoot ("snappy", "swift")
|
|
* - Fading header gradient pseudo-element
|
|
* - Small font sizes (14px body) for editorial/compact aesthetic
|
|
*/
|
|
|
|
/* ========================================================================
|
|
JetBrainsMono Nerd Font Mono — patched with box-drawing glyphs that
|
|
tile perfectly (no gaps). Only load regular 400 to keep payload small.
|
|
Source: github.com/ryanoasis/nerd-fonts
|
|
======================================================================== */
|
|
|
|
@font-face {
|
|
font-family: "JetBrainsMono NF Mono";
|
|
font-style: normal;
|
|
font-weight: 400;
|
|
font-display: swap;
|
|
src: url("https://raw.githubusercontent.com/ryanoasis/nerd-fonts/refs/tags/v3.3.0/patched-fonts/JetBrainsMono/Ligatures/Regular/JetBrainsMonoNerdFontMono-Regular.ttf") format("truetype");
|
|
}
|
|
|
|
/* ========================================================================
|
|
Design tokens
|
|
======================================================================== */
|
|
|
|
.liveline-page {
|
|
--ll-font-primary: "Inter var", "Inter", system-ui, -apple-system,
|
|
BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
--ll-font-secondary: "Newsreader", Georgia, "Times New Roman", serif;
|
|
--ll-font-code: "JetBrainsMono NF Mono", "JetBrains Mono", "SF Mono",
|
|
"SFMono-Regular", "Consolas", "Liberation Mono", Menlo, Courier, monospace;
|
|
|
|
/* Bleed: code blocks & images extend beyond prose column.
|
|
36px = 8px div padding-left + 28px line-number span (border-box includes its 20px paddingRight) */
|
|
--ll-bleed: 36px;
|
|
|
|
/* Spacing scale */
|
|
--ll-spacing-xxs: 0.5rem;
|
|
--ll-spacing-xs: 1rem;
|
|
--ll-spacing-sm: 1.5rem;
|
|
--ll-spacing-md: 2rem;
|
|
--ll-spacing-lg: 2.5rem;
|
|
--ll-spacing-xl: 3rem;
|
|
--ll-spacing-xxl: 3.5rem;
|
|
|
|
/* Timing */
|
|
--ll-duration-snappy: 220ms;
|
|
--ll-ease-snappy: cubic-bezier(0.175, 0.885, 0.32, 1.1);
|
|
--ll-duration-swift: 800ms;
|
|
--ll-ease-swift: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
--ll-duration-smooth: 300ms;
|
|
--ll-ease-smooth: cubic-bezier(0.19, 1, 0.22, 1);
|
|
|
|
/* Colors - near-black, not pure black */
|
|
--ll-text-primary: rgb(17, 17, 17);
|
|
--ll-text-secondary: rgba(0, 0, 0, 0.4);
|
|
--ll-text-tertiary: rgba(0, 0, 0, 0.25);
|
|
--ll-text-muted: rgba(0, 0, 0, 0.35);
|
|
--ll-text-hover: rgba(0, 0, 0, 0.7);
|
|
--ll-bg: #fff;
|
|
--ll-border: #e3e3e3;
|
|
--ll-divider: rgb(242, 242, 242);
|
|
--ll-code-bg: rgba(0, 0, 0, 0.02);
|
|
--ll-code-line-nr: rgba(0, 0, 0, 0.15);
|
|
--ll-selection-bg: rgba(0, 0, 0, 0.08);
|
|
|
|
/* Button / back button */
|
|
--ll-btn-bg: #fff;
|
|
--ll-btn-shadow: rgba(0, 0, 0, 0.08) 0px 2px 8px,
|
|
rgba(0, 0, 0, 0.04) 0px 4px 16px,
|
|
rgba(0, 0, 0, 0.06) 0px 0px 0px 1px inset;
|
|
|
|
/* P3 accent colors (wider gamut) */
|
|
--ll-primary: #3e9fff;
|
|
--ll-secondary: #f09637;
|
|
|
|
/* Overlay / glass */
|
|
--ll-overlay-filter: blur(1rem);
|
|
--ll-overlay-bg: hsla(0, 0%, 100%, 0.8);
|
|
--ll-overlay-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04),
|
|
0 1.625rem 3.375rem rgba(0, 0, 0, 0.04),
|
|
0 1rem 2rem rgba(0, 0, 0, 0.03),
|
|
0 0.625rem 1rem rgba(0, 0, 0, 0.024),
|
|
0 0.3125rem 0.5rem rgba(0, 0, 0, 0.02),
|
|
0 0.125rem 0.25rem rgba(0, 0, 0, 0.016),
|
|
0 0 0.125rem rgba(0, 0, 0, 0.01);
|
|
|
|
/* Header fade gradient stops (white) */
|
|
--ll-fade-0: rgb(255, 255, 255);
|
|
--ll-fade-1: rgba(255, 255, 255, 0.737);
|
|
--ll-fade-2: rgba(255, 255, 255, 0.541);
|
|
--ll-fade-3: rgba(255, 255, 255, 0.382);
|
|
--ll-fade-4: rgba(255, 255, 255, 0.278);
|
|
--ll-fade-5: rgba(255, 255, 255, 0.194);
|
|
--ll-fade-6: rgba(255, 255, 255, 0.126);
|
|
--ll-fade-7: rgba(255, 255, 255, 0.075);
|
|
--ll-fade-8: rgba(255, 255, 255, 0.042);
|
|
--ll-fade-9: rgba(255, 255, 255, 0.021);
|
|
--ll-fade-10: rgba(255, 255, 255, 0.008);
|
|
--ll-fade-11: rgba(255, 255, 255, 0.002);
|
|
--ll-fade-12: rgba(255, 255, 255, 0);
|
|
}
|
|
|
|
/* Dark mode overrides — plain CSS, not processed by Tailwind.
|
|
Uses .dark ancestor selector matching @custom-variant dark in globals.css */
|
|
.dark .liveline-page {
|
|
--ll-text-primary: rgba(255, 255, 255, 0.9);
|
|
--ll-text-secondary: rgba(255, 255, 255, 0.45);
|
|
--ll-text-tertiary: rgba(255, 255, 255, 0.25);
|
|
--ll-text-muted: rgba(255, 255, 255, 0.35);
|
|
--ll-text-hover: rgba(255, 255, 255, 0.7);
|
|
--ll-bg: rgb(17, 17, 17);
|
|
--ll-border: rgba(255, 255, 255, 0.1);
|
|
--ll-divider: rgba(255, 255, 255, 0.06);
|
|
--ll-code-bg: rgba(255, 255, 255, 0.05);
|
|
--ll-code-line-nr: rgba(255, 255, 255, 0.15);
|
|
--ll-selection-bg: rgba(255, 255, 255, 0.1);
|
|
|
|
--ll-btn-bg: rgb(30, 30, 30);
|
|
--ll-btn-shadow: rgba(255, 255, 255, 0.05) 0px 2px 8px,
|
|
rgba(255, 255, 255, 0.02) 0px 4px 16px,
|
|
rgba(255, 255, 255, 0.06) 0px 0px 0px 1px inset;
|
|
|
|
--ll-primary: #60a5fa;
|
|
--ll-secondary: #f5a623;
|
|
|
|
--ll-overlay-bg: hsla(0, 0%, 7%, 0.8);
|
|
--ll-overlay-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06),
|
|
0 1.625rem 3.375rem rgba(0, 0, 0, 0.3),
|
|
0 1rem 2rem rgba(0, 0, 0, 0.2),
|
|
0 0.625rem 1rem rgba(0, 0, 0, 0.15),
|
|
0 0.3125rem 0.5rem rgba(0, 0, 0, 0.1),
|
|
0 0.125rem 0.25rem rgba(0, 0, 0, 0.08),
|
|
0 0 0.125rem rgba(0, 0, 0, 0.05);
|
|
|
|
/* Header fade gradient stops (dark) */
|
|
--ll-fade-0: rgb(17, 17, 17);
|
|
--ll-fade-1: rgba(17, 17, 17, 0.737);
|
|
--ll-fade-2: rgba(17, 17, 17, 0.541);
|
|
--ll-fade-3: rgba(17, 17, 17, 0.382);
|
|
--ll-fade-4: rgba(17, 17, 17, 0.278);
|
|
--ll-fade-5: rgba(17, 17, 17, 0.194);
|
|
--ll-fade-6: rgba(17, 17, 17, 0.126);
|
|
--ll-fade-7: rgba(17, 17, 17, 0.075);
|
|
--ll-fade-8: rgba(17, 17, 17, 0.042);
|
|
--ll-fade-9: rgba(17, 17, 17, 0.021);
|
|
--ll-fade-10: rgba(17, 17, 17, 0.008);
|
|
--ll-fade-11: rgba(17, 17, 17, 0.002);
|
|
--ll-fade-12: rgba(17, 17, 17, 0);
|
|
}
|
|
|
|
@supports (color: color(display-p3 1 1 1)) {
|
|
.liveline-page {
|
|
--ll-primary: color(display-p3 0.243137 0.623529 1 / 1);
|
|
--ll-secondary: color(display-p3 0.941176 0.588235 0.215686 / 1);
|
|
}
|
|
}
|
|
|
|
/* ========================================================================
|
|
Selection
|
|
======================================================================== */
|
|
|
|
.liveline-page {
|
|
font-optical-sizing: auto;
|
|
font-feature-settings: "liga" 1, "calt" 1;
|
|
}
|
|
|
|
.liveline-page ::selection {
|
|
background: var(--ll-selection-bg);
|
|
}
|
|
|
|
/* ========================================================================
|
|
Stagger-in animation
|
|
======================================================================== */
|
|
|
|
@keyframes ll-stagger-in {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateY(8px);
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.liveline-article > * {
|
|
animation: ll-stagger-in 0.5s ease both;
|
|
}
|
|
|
|
.liveline-article > :nth-child(1) { animation-delay: 0s; }
|
|
.liveline-article > :nth-child(2) { animation-delay: 0.05s; }
|
|
.liveline-article > :nth-child(3) { animation-delay: 0.1s; }
|
|
.liveline-article > :nth-child(4) { animation-delay: 0.15s; }
|
|
.liveline-article > :nth-child(5) { animation-delay: 0.2s; }
|
|
.liveline-article > :nth-child(6) { animation-delay: 0.25s; }
|
|
.liveline-article > :nth-child(7) { animation-delay: 0.3s; }
|
|
.liveline-article > :nth-child(8) { animation-delay: 0.35s; }
|
|
.liveline-article > :nth-child(n + 9) { animation-delay: 0.4s; }
|
|
|
|
/* ========================================================================
|
|
Bleed — code blocks and images extend beyond the prose column so that
|
|
the code text (after line numbers) aligns with the prose left edge.
|
|
36px = 8px div padding-left + 28px line-number span (border-box, includes padding).
|
|
======================================================================== */
|
|
|
|
.ll-bleed {
|
|
margin-left: calc(-1 * var(--ll-bleed));
|
|
margin-right: calc(-1 * var(--ll-bleed));
|
|
}
|
|
|
|
@media (max-width: 650px) {
|
|
.ll-bleed {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
}
|
|
|
|
/* ========================================================================
|
|
Inline code — subtle background pill via ::before pseudo-element.
|
|
Matches benji.org/liveline: position relative, ::before with absolute
|
|
inset creates a pill that extends slightly above/below the text.
|
|
======================================================================== */
|
|
|
|
.ll-inline-code {
|
|
position: relative;
|
|
display: inline-block;
|
|
font-family: var(--ll-font-code);
|
|
font-size: 12.6px;
|
|
font-weight: 460;
|
|
line-height: 12.6px;
|
|
letter-spacing: -0.09px;
|
|
padding: 1.6px 3.2px;
|
|
color: rgba(0, 0, 0, 0.75);
|
|
}
|
|
|
|
.ll-inline-code::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: -1.26px 0;
|
|
background: rgba(0, 0, 0, 0.04);
|
|
border-radius: 4px;
|
|
z-index: -1;
|
|
}
|
|
|
|
.dark .ll-inline-code {
|
|
color: rgba(255, 255, 255, 0.75);
|
|
}
|
|
|
|
.dark .ll-inline-code::before {
|
|
background: rgba(255, 255, 255, 0.08);
|
|
}
|
|
|
|
/* ========================================================================
|
|
Header fade gradient
|
|
======================================================================== */
|
|
|
|
.liveline-page::before {
|
|
content: "";
|
|
pointer-events: none;
|
|
z-index: 9;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 8rem;
|
|
background: linear-gradient(
|
|
var(--ll-fade-0) 0px,
|
|
var(--ll-fade-1) 19%,
|
|
var(--ll-fade-2) 34%,
|
|
var(--ll-fade-3) 47%,
|
|
var(--ll-fade-4) 56.5%,
|
|
var(--ll-fade-5) 65%,
|
|
var(--ll-fade-6) 73%,
|
|
var(--ll-fade-7) 80.2%,
|
|
var(--ll-fade-8) 86.1%,
|
|
var(--ll-fade-9) 91%,
|
|
var(--ll-fade-10) 95.2%,
|
|
var(--ll-fade-11) 98.2%,
|
|
var(--ll-fade-12) 100%
|
|
);
|
|
}
|