website: move editorial tokens to globals.css, remove --ll- prefix
All CSS variables previously scoped under .liveline-page with --ll- prefix are now at :root in globals.css with no prefix. Four names conflicting with shadcn were renamed: --brand-primary, --brand-secondary, --link-accent, --page-border. Class renames: .liveline-page → .editorial-page, .liveline-article → .editorial-article, .ll-bleed → .bleed, .ll-inline-code → .inline-code, .ll-prose → .editorial-prose. Filenames kept as-is for this commit to isolate the rename.
This commit is contained in:
+2
-2
@@ -38,8 +38,8 @@ Dark mode uses `prefers-color-scheme` media query, configured in `globals.css`:
|
||||
```css
|
||||
/* globals.css — this is the Tailwind entry point */
|
||||
@import "tailwindcss";
|
||||
@import "./liveline.css"; /* these get Tailwind processing */
|
||||
@import "./liveline-prism.css";
|
||||
@import "./liveline.css"; /* editorial page styles (class names, layout) */
|
||||
@import "./liveline-prism.css"; /* prism syntax highlighting */
|
||||
@custom-variant dark (@media (prefers-color-scheme: dark));
|
||||
```
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Editorial markdown components for the liveline-style pages.
|
||||
* Extracted from liveline.tsx so page files are content-only.
|
||||
* Editorial markdown components.
|
||||
*
|
||||
* All components use CSS variables from liveline.css (--ll-*).
|
||||
* Import liveline.css and liveline-prism.css in the page file.
|
||||
* All components use CSS variables from globals.css (no prefix).
|
||||
* Conflicting names with shadcn: --brand-primary, --brand-secondary,
|
||||
* --link-accent, --page-border.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
@@ -88,22 +88,22 @@ export function TableOfContents({
|
||||
lineHeight: "20px",
|
||||
letterSpacing: "-0.09px",
|
||||
padding: "4px 0",
|
||||
color: "var(--ll-text-primary)",
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
color: "var(--text-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = "var(--ll-text-hover)";
|
||||
e.currentTarget.style.color = "var(--text-hover)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = "var(--ll-text-primary)";
|
||||
e.currentTarget.style.color = "var(--text-primary)";
|
||||
}}
|
||||
>
|
||||
{logo ?? "index"}
|
||||
</a>
|
||||
{items.map((item) => {
|
||||
const isActive = `#${activeId}` === item.href;
|
||||
const defaultColor = isActive ? "var(--ll-text-primary)" : "var(--ll-text-secondary)";
|
||||
const defaultColor = isActive ? "var(--text-primary)" : "var(--text-secondary)";
|
||||
return (
|
||||
<a
|
||||
key={item.href}
|
||||
@@ -116,12 +116,12 @@ export function TableOfContents({
|
||||
letterSpacing: "-0.04px",
|
||||
padding: "5px 0",
|
||||
color: defaultColor,
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
transition: "color 0.15s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!isActive) {
|
||||
e.currentTarget.style.color = "var(--ll-text-hover)";
|
||||
e.currentTarget.style.color = "var(--text-hover)";
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
@@ -147,17 +147,17 @@ export function BackButton() {
|
||||
href="/"
|
||||
className="fixed top-5 right-5 z-[100000] flex items-center justify-center w-10 h-10 rounded-full no-underline"
|
||||
style={{
|
||||
background: "var(--ll-btn-bg)",
|
||||
color: "var(--ll-text-secondary)",
|
||||
boxShadow: "var(--ll-btn-shadow)",
|
||||
background: "var(--btn-bg)",
|
||||
color: "var(--text-secondary)",
|
||||
boxShadow: "var(--btn-shadow)",
|
||||
transition: "color 0.15s, transform 0.15s",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = "var(--ll-text-hover)";
|
||||
e.currentTarget.style.color = "var(--text-hover)";
|
||||
e.currentTarget.style.transform = "scale(1.05)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = "var(--ll-text-secondary)";
|
||||
e.currentTarget.style.color = "var(--text-secondary)";
|
||||
e.currentTarget.style.transform = "scale(1)";
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
@@ -190,12 +190,12 @@ export function SectionHeading({ id, children }: { id: string; children: React.R
|
||||
id={id}
|
||||
className="scroll-mt-[5.25rem]"
|
||||
style={{
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
fontSize: "14px",
|
||||
fontWeight: 560,
|
||||
lineHeight: "20px",
|
||||
letterSpacing: "-0.09px",
|
||||
color: "var(--ll-text-primary)",
|
||||
color: "var(--text-primary)",
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
@@ -206,7 +206,7 @@ export function SectionHeading({ id, children }: { id: string; children: React.R
|
||||
}}
|
||||
>
|
||||
<span style={{ whiteSpace: "nowrap" }}>{children}</span>
|
||||
<span style={{ flex: 1, height: "1px", background: "var(--ll-divider)" }} />
|
||||
<span style={{ flex: 1, height: "1px", background: "var(--divider)" }} />
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
@@ -214,14 +214,14 @@ export function SectionHeading({ id, children }: { id: string; children: React.R
|
||||
export function P({ children, className = "" }: { children: React.ReactNode; className?: string }) {
|
||||
return (
|
||||
<p
|
||||
className={`ll-prose ${className}`}
|
||||
className={`editorial-prose ${className}`}
|
||||
style={{
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
fontSize: "14px",
|
||||
fontWeight: 475,
|
||||
lineHeight: "22px",
|
||||
letterSpacing: "-0.09px",
|
||||
color: "var(--ll-text-primary)",
|
||||
color: "var(--text-primary)",
|
||||
opacity: 0.82,
|
||||
margin: 0,
|
||||
}}
|
||||
@@ -235,13 +235,13 @@ export function Caption({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
fontSize: "12px",
|
||||
fontWeight: 475,
|
||||
textAlign: "center",
|
||||
lineHeight: "20px",
|
||||
letterSpacing: "-0.09px",
|
||||
color: "var(--ll-text-secondary)",
|
||||
color: "var(--text-secondary)",
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
@@ -257,7 +257,7 @@ export function A({ href, children }: { href: string; children: React.ReactNode
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{
|
||||
color: "var(--ll-accent, #0969da)",
|
||||
color: "var(--link-accent, #0969da)",
|
||||
fontWeight: 600,
|
||||
textDecoration: "none",
|
||||
}}
|
||||
@@ -275,7 +275,7 @@ export function A({ href, children }: { href: string; children: React.ReactNode
|
||||
|
||||
export function Code({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<code className="ll-inline-code">
|
||||
<code className="inline-code">
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
@@ -288,7 +288,7 @@ export function Code({ children }: { children: React.ReactNode }) {
|
||||
export function Divider() {
|
||||
return (
|
||||
<div style={{ padding: "24px 0", display: "flex", alignItems: "center" }}>
|
||||
<div style={{ height: "1px", background: "var(--ll-divider)", flex: 1 }} />
|
||||
<div style={{ height: "1px", background: "var(--divider)", flex: 1 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -307,12 +307,12 @@ export function OL({ children }: { children: React.ReactNode }) {
|
||||
<ol
|
||||
className="m-0 pl-5"
|
||||
style={{
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
fontSize: "14px",
|
||||
fontWeight: 475,
|
||||
lineHeight: "20px",
|
||||
letterSpacing: "-0.09px",
|
||||
color: "var(--ll-text-primary)",
|
||||
color: "var(--text-primary)",
|
||||
listStyleType: "decimal",
|
||||
}}
|
||||
>
|
||||
@@ -326,12 +326,12 @@ export function List({ children }: { children: React.ReactNode }) {
|
||||
<ul
|
||||
className="m-0 pl-5"
|
||||
style={{
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
fontSize: "14px",
|
||||
fontWeight: 475,
|
||||
lineHeight: "20px",
|
||||
letterSpacing: "-0.09px",
|
||||
color: "var(--ll-text-primary)",
|
||||
color: "var(--text-primary)",
|
||||
listStyleType: "disc",
|
||||
}}
|
||||
>
|
||||
@@ -359,12 +359,12 @@ export function CodeBlock({ children, lang = "jsx" }: { children: string; lang?:
|
||||
}, [children]);
|
||||
|
||||
return (
|
||||
<figure className="m-0 ll-bleed">
|
||||
<figure className="m-0 bleed">
|
||||
<div className="relative">
|
||||
<pre
|
||||
className="overflow-x-auto"
|
||||
style={{
|
||||
// background: "var(--ll-code-bg)",
|
||||
// background: "var(--code-bg)",
|
||||
borderRadius: "8px",
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
@@ -374,12 +374,12 @@ export function CodeBlock({ children, lang = "jsx" }: { children: string; lang?:
|
||||
className="flex"
|
||||
style={{
|
||||
padding: "12px 8px 8px",
|
||||
fontFamily: "var(--ll-font-code)",
|
||||
fontFamily: "var(--font-code)",
|
||||
fontSize: "12px",
|
||||
fontWeight: 400,
|
||||
lineHeight: "18px",
|
||||
letterSpacing: "normal",
|
||||
color: "var(--ll-text-primary)",
|
||||
color: "var(--text-primary)",
|
||||
tabSize: 2,
|
||||
}}
|
||||
>
|
||||
@@ -387,7 +387,7 @@ export function CodeBlock({ children, lang = "jsx" }: { children: string; lang?:
|
||||
className="select-none shrink-0"
|
||||
aria-hidden="true"
|
||||
style={{
|
||||
color: "var(--ll-code-line-nr)",
|
||||
color: "var(--code-line-nr)",
|
||||
textAlign: "right",
|
||||
paddingRight: "20px",
|
||||
width: "36px",
|
||||
@@ -422,7 +422,7 @@ export function CodeBlock({ children, lang = "jsx" }: { children: string; lang?:
|
||||
|
||||
export function ChartPlaceholder({ height = 200, label }: { height?: number; label?: string }) {
|
||||
return (
|
||||
<div className="ll-bleed">
|
||||
<div className="bleed">
|
||||
<div
|
||||
className="w-full overflow-hidden relative"
|
||||
style={{
|
||||
@@ -462,7 +462,7 @@ export function ChartPlaceholder({ height = 200, label }: { height?: number; lab
|
||||
style={{
|
||||
background: "rgba(59, 130, 246, 0.15)",
|
||||
color: "#3b82f6",
|
||||
fontFamily: "var(--ll-font-code)",
|
||||
fontFamily: "var(--font-code)",
|
||||
fontWeight: 475,
|
||||
fontSize: "11px",
|
||||
}}
|
||||
@@ -493,10 +493,10 @@ export function ComparisonTable({
|
||||
{title && (
|
||||
<div
|
||||
style={{
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
fontSize: "11px",
|
||||
fontWeight: 400,
|
||||
color: "var(--ll-text-muted)",
|
||||
color: "var(--text-muted)",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.02em",
|
||||
padding: "0 0 6px",
|
||||
@@ -523,9 +523,9 @@ export function ComparisonTable({
|
||||
padding: "4.8px 12px 4.8px 0",
|
||||
fontSize: "11px",
|
||||
fontWeight: 400,
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
color: "var(--ll-text-muted)",
|
||||
borderBottom: "1px solid var(--ll-border)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
color: "var(--text-muted)",
|
||||
borderBottom: "1px solid var(--page-border)",
|
||||
}}
|
||||
>
|
||||
{header}
|
||||
@@ -543,9 +543,9 @@ export function ComparisonTable({
|
||||
padding: "4.8px 12px 4.8px 0",
|
||||
fontSize: "11px",
|
||||
fontWeight: 500,
|
||||
fontFamily: "var(--ll-font-code)",
|
||||
color: "var(--ll-text-primary)",
|
||||
borderBottom: "1px solid var(--ll-border)",
|
||||
fontFamily: "var(--font-code)",
|
||||
color: "var(--text-primary)",
|
||||
borderBottom: "1px solid var(--page-border)",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
@@ -556,9 +556,9 @@ export function ComparisonTable({
|
||||
padding: "4.8px 12px 4.8px 0",
|
||||
fontSize: "11px",
|
||||
fontWeight: 500,
|
||||
fontFamily: "var(--ll-font-code)",
|
||||
color: "var(--ll-text-primary)",
|
||||
borderBottom: "1px solid var(--ll-border)",
|
||||
fontFamily: "var(--font-code)",
|
||||
color: "var(--text-primary)",
|
||||
borderBottom: "1px solid var(--page-border)",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
@@ -569,9 +569,9 @@ export function ComparisonTable({
|
||||
padding: "4.8px 12px 4.8px 0",
|
||||
fontSize: "11px",
|
||||
fontWeight: 500,
|
||||
fontFamily: "var(--ll-font-code)",
|
||||
color: "var(--ll-text-primary)",
|
||||
borderBottom: "1px solid var(--ll-border)",
|
||||
fontFamily: "var(--font-code)",
|
||||
color: "var(--text-primary)",
|
||||
borderBottom: "1px solid var(--page-border)",
|
||||
}}
|
||||
>
|
||||
{us}
|
||||
@@ -600,11 +600,11 @@ export function EditorialPage({
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className="liveline-page relative min-h-screen overflow-x-hidden"
|
||||
className="editorial-page relative min-h-screen overflow-x-hidden"
|
||||
style={{
|
||||
background: "var(--ll-bg)",
|
||||
color: "var(--ll-text-primary)",
|
||||
fontFamily: "var(--ll-font-primary)",
|
||||
background: "var(--bg)",
|
||||
color: "var(--text-primary)",
|
||||
fontFamily: "var(--font-primary)",
|
||||
WebkitFontSmoothing: "antialiased",
|
||||
textRendering: "optimizeLegibility",
|
||||
}}
|
||||
@@ -617,7 +617,7 @@ export function EditorialPage({
|
||||
>
|
||||
<div style={{ height: "80px" }} />
|
||||
|
||||
<article className="liveline-article flex flex-col gap-[32px]">
|
||||
<article className="editorial-article flex flex-col gap-[32px]">
|
||||
{children}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Playwriter editorial page — content only.
|
||||
* Components imported from website/src/components/markdown.tsx.
|
||||
* Styles from liveline.css and liveline-prism.css.
|
||||
* Styles from globals.css (editorial tokens) and liveline-prism.css.
|
||||
*/
|
||||
|
||||
import type { MetaFunction } from "react-router";
|
||||
@@ -34,7 +34,7 @@ export const meta: MetaFunction = () => {
|
||||
{ property: "og:image:width", content: "1200" },
|
||||
{ property: "og:image:height", content: "630" },
|
||||
{ property: "og:type", content: "website" },
|
||||
{ property: "og:url", content: "https://playwriter.dev/liveline" },
|
||||
{ property: "og:url", content: "https://playwriter.dev" },
|
||||
{ name: "twitter:card", content: "summary_large_image" },
|
||||
{ name: "twitter:title", content: title },
|
||||
{ name: "twitter:description", content: description },
|
||||
@@ -56,7 +56,7 @@ const tocItems = [
|
||||
{ label: "Security", href: "#security" },
|
||||
];
|
||||
|
||||
export default function LivelinePage() {
|
||||
export default function IndexPage() {
|
||||
return (
|
||||
<EditorialPage toc={tocItems} logo="playwriter">
|
||||
|
||||
@@ -67,7 +67,7 @@ export default function LivelinePage() {
|
||||
<A href="https://github.com/remorses/playwriter">Star on GitHub</A>.
|
||||
</P>
|
||||
|
||||
<div className="ll-bleed" style={{ display: "flex", justifyContent: "center" }}>
|
||||
<div className="bleed" style={{ display: "flex", justifyContent: "center" }}>
|
||||
<img
|
||||
src="/screenshot@2x.png"
|
||||
alt="Playwriter controlling Chrome with accessibility labels overlay"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
:root {
|
||||
/* ===== shadcn/ui tokens ===== */
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
@@ -40,9 +41,90 @@
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.871 0.006 286.286);
|
||||
|
||||
/* ===== Editorial design tokens ===== */
|
||||
--font-primary: "Inter var", "Inter", system-ui, -apple-system,
|
||||
BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-secondary: "Newsreader", Georgia, "Times New Roman", serif;
|
||||
--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.
|
||||
44px = 8px div padding-left + 36px line-number width */
|
||||
--bleed: 44px;
|
||||
|
||||
/* Spacing scale */
|
||||
--spacing-xxs: 0.5rem;
|
||||
--spacing-xs: 1rem;
|
||||
--spacing-sm: 1.5rem;
|
||||
--spacing-md: 2rem;
|
||||
--spacing-lg: 2.5rem;
|
||||
--spacing-xl: 3rem;
|
||||
--spacing-xxl: 3.5rem;
|
||||
|
||||
/* Timing */
|
||||
--duration-snappy: 220ms;
|
||||
--ease-snappy: cubic-bezier(0.175, 0.885, 0.32, 1.1);
|
||||
--duration-swift: 800ms;
|
||||
--ease-swift: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
--duration-smooth: 300ms;
|
||||
--ease-smooth: cubic-bezier(0.19, 1, 0.22, 1);
|
||||
|
||||
/* Colors — near-black, not pure black */
|
||||
--text-primary: rgb(17, 17, 17);
|
||||
--text-secondary: rgba(0, 0, 0, 0.4);
|
||||
--text-tertiary: rgba(0, 0, 0, 0.25);
|
||||
--text-muted: rgba(0, 0, 0, 0.5);
|
||||
--text-hover: rgba(0, 0, 0, 0.7);
|
||||
--bg: #fff;
|
||||
--page-border: #e3e3e3;
|
||||
--divider: rgb(242, 242, 242);
|
||||
--code-bg: rgba(0, 0, 0, 0.02);
|
||||
--code-line-nr: rgba(0, 0, 0, 0.3);
|
||||
--selection-bg: rgba(0, 0, 0, 0.08);
|
||||
|
||||
/* Button / back button */
|
||||
--btn-bg: #fff;
|
||||
--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;
|
||||
|
||||
/* Link accent color (renamed from --accent to avoid shadcn conflict) */
|
||||
--link-accent: #0969da;
|
||||
|
||||
/* P3 accent colors (renamed from --primary/--secondary to avoid shadcn conflict) */
|
||||
--brand-primary: #3e9fff;
|
||||
--brand-secondary: #f09637;
|
||||
|
||||
/* Overlay / glass */
|
||||
--overlay-filter: blur(1rem);
|
||||
--overlay-bg: hsla(0, 0%, 100%, 0.8);
|
||||
--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) */
|
||||
--fade-0: rgb(255, 255, 255);
|
||||
--fade-1: rgba(255, 255, 255, 0.737);
|
||||
--fade-2: rgba(255, 255, 255, 0.541);
|
||||
--fade-3: rgba(255, 255, 255, 0.382);
|
||||
--fade-4: rgba(255, 255, 255, 0.278);
|
||||
--fade-5: rgba(255, 255, 255, 0.194);
|
||||
--fade-6: rgba(255, 255, 255, 0.126);
|
||||
--fade-7: rgba(255, 255, 255, 0.075);
|
||||
--fade-8: rgba(255, 255, 255, 0.042);
|
||||
--fade-9: rgba(255, 255, 255, 0.021);
|
||||
--fade-10: rgba(255, 255, 255, 0.008);
|
||||
--fade-11: rgba(255, 255, 255, 0.002);
|
||||
--fade-12: rgba(255, 255, 255, 0);
|
||||
|
||||
/* Dark mode overrides — uses prefers-color-scheme media query
|
||||
via @custom-variant dark above */
|
||||
@variant dark {
|
||||
/* shadcn/ui dark */
|
||||
--background: oklch(0.21 0.006 285.885);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
@@ -75,6 +157,60 @@
|
||||
--sidebar-accent-foreground: oklch(0.871 0.006 286.286);
|
||||
--sidebar-border: oklch(0.274 0.006 286.033);
|
||||
--sidebar-ring: oklch(0.442 0.017 285.786);
|
||||
|
||||
/* Editorial dark */
|
||||
--text-primary: rgba(255, 255, 255, 0.9);
|
||||
--text-secondary: rgba(255, 255, 255, 0.45);
|
||||
--text-tertiary: rgba(255, 255, 255, 0.25);
|
||||
--text-muted: rgba(255, 255, 255, 0.35);
|
||||
--text-hover: rgba(255, 255, 255, 0.7);
|
||||
--bg: rgb(17, 17, 17);
|
||||
--page-border: rgba(255, 255, 255, 0.1);
|
||||
--divider: rgba(255, 255, 255, 0.06);
|
||||
--code-bg: rgba(255, 255, 255, 0.05);
|
||||
--code-line-nr: rgba(255, 255, 255, 0.3);
|
||||
--selection-bg: rgba(255, 255, 255, 0.1);
|
||||
|
||||
--btn-bg: rgb(30, 30, 30);
|
||||
--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;
|
||||
|
||||
--link-accent: #58a6ff;
|
||||
--brand-primary: #60a5fa;
|
||||
--brand-secondary: #f5a623;
|
||||
|
||||
--overlay-bg: hsla(0, 0%, 7%, 0.8);
|
||||
--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) */
|
||||
--fade-0: rgb(17, 17, 17);
|
||||
--fade-1: rgba(17, 17, 17, 0.737);
|
||||
--fade-2: rgba(17, 17, 17, 0.541);
|
||||
--fade-3: rgba(17, 17, 17, 0.382);
|
||||
--fade-4: rgba(17, 17, 17, 0.278);
|
||||
--fade-5: rgba(17, 17, 17, 0.194);
|
||||
--fade-6: rgba(17, 17, 17, 0.126);
|
||||
--fade-7: rgba(17, 17, 17, 0.075);
|
||||
--fade-8: rgba(17, 17, 17, 0.042);
|
||||
--fade-9: rgba(17, 17, 17, 0.021);
|
||||
--fade-10: rgba(17, 17, 17, 0.008);
|
||||
--fade-11: rgba(17, 17, 17, 0.002);
|
||||
--fade-12: rgba(17, 17, 17, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* P3 wide-gamut accent colors */
|
||||
@supports (color: color(display-p3 1 1 1)) {
|
||||
:root {
|
||||
--brand-primary: color(display-p3 0.243137 0.623529 1 / 1);
|
||||
--brand-secondary: color(display-p3 0.941176 0.588235 0.215686 / 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ pre[class*="language-"] {
|
||||
color: #1e293b;
|
||||
background: none;
|
||||
text-shadow: none;
|
||||
font-family: var(--ll-font-code, "SF Mono", "SFMono-Regular", "Consolas",
|
||||
font-family: var(--font-code, "SF Mono", "SFMono-Regular", "Consolas",
|
||||
"Liberation Mono", Menlo, Courier, monospace);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
+48
-188
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Liveline page styles - recreating benji.org's editorial design system.
|
||||
* Editorial page styles.
|
||||
*
|
||||
* Key design decisions from the original:
|
||||
* Key design decisions:
|
||||
* - 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
|
||||
@@ -12,6 +12,10 @@
|
||||
* - Custom cubic-bezier easings with slight overshoot ("snappy", "swift")
|
||||
* - Fading header gradient pseudo-element
|
||||
* - Small font sizes (14px body) for editorial/compact aesthetic
|
||||
*
|
||||
* Design tokens are defined in globals.css :root (no prefix).
|
||||
* Conflicting names with shadcn: --brand-primary, --brand-secondary,
|
||||
* --link-accent, --page-border.
|
||||
*/
|
||||
|
||||
/* ========================================================================
|
||||
@@ -28,166 +32,22 @@
|
||||
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.
|
||||
44px = 8px div padding-left + 36px line-number width (border-box, includes 20px paddingRight) */
|
||||
--ll-bleed: 44px;
|
||||
|
||||
/* 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.5);
|
||||
--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.3);
|
||||
--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;
|
||||
|
||||
/* Link accent color */
|
||||
--ll-accent: #0969da;
|
||||
|
||||
/* 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 — @variant dark works here because globals.css
|
||||
imports this file via CSS @import (part of Tailwind compilation chain).
|
||||
Must be nested inside a selector, not at the top level. */
|
||||
.liveline-page {
|
||||
@variant dark {
|
||||
--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.3);
|
||||
--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-accent: #58a6ff;
|
||||
--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 {
|
||||
.editorial-page {
|
||||
font-optical-sizing: auto;
|
||||
font-feature-settings: "liga" 1, "calt" 1;
|
||||
}
|
||||
|
||||
.liveline-page ::selection {
|
||||
background: var(--ll-selection-bg);
|
||||
.editorial-page ::selection {
|
||||
background: var(--selection-bg);
|
||||
}
|
||||
|
||||
/* Bold and inline code inside prose punch through the reduced opacity */
|
||||
.ll-prose strong,
|
||||
.ll-prose .ll-inline-code {
|
||||
.editorial-prose strong,
|
||||
.editorial-prose .inline-code {
|
||||
opacity: calc(1 / 0.82);
|
||||
}
|
||||
|
||||
@@ -195,7 +55,7 @@
|
||||
Stagger-in animation
|
||||
======================================================================== */
|
||||
|
||||
@keyframes ll-stagger-in {
|
||||
@keyframes stagger-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
@@ -207,19 +67,19 @@
|
||||
}
|
||||
|
||||
/*
|
||||
.liveline-article > * {
|
||||
animation: ll-stagger-in 0.5s ease both;
|
||||
.editorial-article > * {
|
||||
animation: 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; }
|
||||
.editorial-article > :nth-child(1) { animation-delay: 0s; }
|
||||
.editorial-article > :nth-child(2) { animation-delay: 0.05s; }
|
||||
.editorial-article > :nth-child(3) { animation-delay: 0.1s; }
|
||||
.editorial-article > :nth-child(4) { animation-delay: 0.15s; }
|
||||
.editorial-article > :nth-child(5) { animation-delay: 0.2s; }
|
||||
.editorial-article > :nth-child(6) { animation-delay: 0.25s; }
|
||||
.editorial-article > :nth-child(7) { animation-delay: 0.3s; }
|
||||
.editorial-article > :nth-child(8) { animation-delay: 0.35s; }
|
||||
.editorial-article > :nth-child(n + 9) { animation-delay: 0.4s; }
|
||||
*/
|
||||
|
||||
/* ========================================================================
|
||||
@@ -228,13 +88,13 @@
|
||||
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));
|
||||
.bleed {
|
||||
margin-left: calc(-1 * var(--bleed));
|
||||
margin-right: calc(-1 * var(--bleed));
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.ll-bleed {
|
||||
.bleed {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -242,14 +102,14 @@
|
||||
|
||||
/* ========================================================================
|
||||
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.
|
||||
Position relative, ::before with absolute inset creates a pill that
|
||||
extends slightly above/below the text.
|
||||
======================================================================== */
|
||||
|
||||
.ll-inline-code {
|
||||
.inline-code {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-family: var(--ll-font-code);
|
||||
font-family: var(--font-code);
|
||||
font-size: 12.6px;
|
||||
font-weight: 500;
|
||||
line-height: 12.6px;
|
||||
@@ -258,7 +118,7 @@
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.ll-inline-code::before {
|
||||
.inline-code::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -1.26px 0;
|
||||
@@ -267,13 +127,13 @@
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.ll-inline-code {
|
||||
.inline-code {
|
||||
@variant dark {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
.ll-inline-code::before {
|
||||
.inline-code::before {
|
||||
@variant dark {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
@@ -283,7 +143,7 @@
|
||||
Header fade gradient
|
||||
======================================================================== */
|
||||
|
||||
.liveline-page::before {
|
||||
.editorial-page::before {
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
z-index: 9;
|
||||
@@ -293,18 +153,18 @@
|
||||
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%
|
||||
var(--fade-0) 0px,
|
||||
var(--fade-1) 19%,
|
||||
var(--fade-2) 34%,
|
||||
var(--fade-3) 47%,
|
||||
var(--fade-4) 56.5%,
|
||||
var(--fade-5) 65%,
|
||||
var(--fade-6) 73%,
|
||||
var(--fade-7) 80.2%,
|
||||
var(--fade-8) 86.1%,
|
||||
var(--fade-9) 91%,
|
||||
var(--fade-10) 95.2%,
|
||||
var(--fade-11) 98.2%,
|
||||
var(--fade-12) 100%
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user