diff --git a/src/App.tsx b/src/App.tsx index 28b1af5..3b8f2ac 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,12 @@ import { } from "./config"; import { arweaveUrl, getReadableDate, loadManifest, loadPostContent } from "./lib"; import type { Frontmatter, ManifestPost } from "./types"; +import load1 from "./load1.png"; +import load2 from "./load2.png"; +import load3 from "./load3.png"; +import load4 from "./load4.png"; +import load5 from "./load5.png"; +import load6 from "./load6.png"; type LoadState = "idle" | "loading" | "error"; @@ -21,6 +27,9 @@ type MetaInput = { modifiedTime?: string; }; +const LOADER_FRAMES = [load1, load2, load3, load4, load5, load6]; +const FORCE_LOADER_PREVIEW = false; + const toAbsoluteUrl = (path = "/"): string => new URL(path, BLOG_SITE_URL).toString(); const setMetaTag = (attribute: "name" | "property", key: string, content?: string): void => { @@ -91,9 +100,19 @@ const usePageMetadata = ({ }; function BlisterLoader() { + const [frame, setFrame] = useState(0); + + useEffect(() => { + const timer = window.setInterval(() => { + setFrame((value) => (value + 1) % LOADER_FRAMES.length); + }, 70); + + return () => window.clearInterval(timer); + }, []); + return (
- +
); } @@ -137,18 +156,23 @@ function App() { return (
- -
+ {FORCE_LOADER_PREVIEW ? ( + + ) : ( } /> + )}
); @@ -341,9 +366,15 @@ function PostPage({ {updatedDate && Updated {updatedDate}} {post.readingTime && {post.readingTime} min read} {post.wordCount && {post.wordCount} words} + {post.postTxId.slice(0, 8)}... [permalink] + + + [arweave] + + {bannerTxId && ( diff --git a/src/favicon.ico b/src/favicon.ico index 18dbc2d..24ac122 100644 Binary files a/src/favicon.ico and b/src/favicon.ico differ diff --git a/src/load1.png b/src/load1.png new file mode 100644 index 0000000..2ad15bc Binary files /dev/null and b/src/load1.png differ diff --git a/src/load2.png b/src/load2.png new file mode 100644 index 0000000..53c853a Binary files /dev/null and b/src/load2.png differ diff --git a/src/load3.png b/src/load3.png new file mode 100644 index 0000000..166f331 Binary files /dev/null and b/src/load3.png differ diff --git a/src/load4.png b/src/load4.png new file mode 100644 index 0000000..54a2553 Binary files /dev/null and b/src/load4.png differ diff --git a/src/load5.png b/src/load5.png new file mode 100644 index 0000000..f1157bb Binary files /dev/null and b/src/load5.png differ diff --git a/src/load6.png b/src/load6.png new file mode 100644 index 0000000..22718e7 Binary files /dev/null and b/src/load6.png differ diff --git a/src/styles.css b/src/styles.css index 8cb14a4..3162db0 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,23 +1,39 @@ +@import url("https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500&display=swap"); + :root { - color: #0a0a0a; + color: #101010; background: #ffffff; - font-family: "IBM Plex Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; - line-height: 1.6; + font-family: "DM Sans", sans-serif; + line-height: 1.55; + --site-max-width: 760px; --brand-red: #f60000; --brand-purple: #9611ff; --brand-blue: #86dafe; --brand-yellow: #fee55f; --brand-green: #33f22f; + --line: #121212; + --line-soft: #d8d8d8; + --muted: #585858; + --accent: #0072c8; } * { box-sizing: border-box; + border-radius: 0; +} + +html, +body, +#root { + min-height: 100%; } body { margin: 0; background: #ffffff; color: #111111; + font-family: "DM Sans", sans-serif; + font-weight: 400; } a { @@ -26,21 +42,32 @@ a { text-underline-offset: 3px; } +a:hover { + color: var(--accent); +} + .page { min-height: 100vh; - padding: 24px; + width: min(var(--site-max-width), 100% - 48px); + margin: 0 auto; + padding: 0 0 48px; } .header { - border-bottom: 2px solid #111111; - padding-bottom: 14px; - margin-bottom: 28px; + border-bottom: 1px solid var(--line); +} + +.header-row { + display: flex; + align-items: center; + min-height: 72px; + padding: 0 24px; } .brand { - font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace; + font-family: "DM Sans", sans-serif; font-size: 1.3rem; - font-weight: 600; + font-weight: 500; letter-spacing: 0.08em; text-decoration: none; display: inline-flex; @@ -87,12 +114,14 @@ a { } .content { - max-width: 860px; - margin: 0 auto; + max-width: 100%; } .status { - font-size: 1rem; + margin: 0; + padding: 24px; + border-top: 1px solid var(--line); + font-size: 0.95rem; } .loader-screen { @@ -101,58 +130,39 @@ a { place-items: center; } -.loader-square { - width: 22px; - height: 22px; - background: var(--brand-red); - animation: blister-flicker 180ms steps(1, end) infinite; -} - -@keyframes blister-flicker { - 0% { - background: var(--brand-red); - } - 20% { - background: var(--brand-purple); - } - 40% { - background: var(--brand-blue); - } - 60% { - background: var(--brand-yellow); - } - 80% { - background: var(--brand-green); - } - 100% { - background: var(--brand-red); - } +.loader-frame { + display: block; + width: min(45px, 11vw); + height: auto; + image-rendering: pixelated; + border-radius: 8px; } .index { - display: grid; - gap: 28px; + border-top: 0; +} + +.post-card { + padding: 22px 24px 26px; + border-bottom: 1px solid var(--line-soft); +} + +.post-card-featured { + border-bottom: 1px solid var(--line-soft); + background: linear-gradient(180deg, #ffffff 0%, #fcfcfc 100%); } .index-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - column-gap: 28px; - row-gap: 28px; } -.post-card { - padding-top: 0; +.index-grid .post-card { + border-bottom: 0; } -.index-grid .post-card:nth-child(n + 3) { - border-top: 1px solid #111111; - padding-top: 18px; -} - -.post-card-featured { - border-top: 0; - padding-top: 0; +.index-grid .post-card:nth-child(odd) { + border-right: 0; } .banner-link { @@ -164,36 +174,43 @@ a { display: block; width: 100%; height: auto; - border: 1px solid #111111; + border: 1px solid var(--line); } .post-title-link { text-decoration: none; } +.post-title-link:hover .post-title { + color: var(--accent); +} + .post-title { margin: 0; - font-size: 1.8rem; - line-height: 1.2; + font-size: clamp(1.32rem, 2.15vw, 1.7rem); + font-weight: 500; + line-height: 1.18; + letter-spacing: -0.02em; } .post-description { - margin: 10px 0 12px; - font-size: 1.04rem; + margin: 12px 0 14px; + font-size: 0.97rem; + color: #222222; } .meta-row { display: flex; flex-wrap: wrap; gap: 0; - font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace; - font-size: 0.86rem; + font-size: 0.82rem; + color: var(--muted); } .meta-row > span + span { position: relative; - margin-left: 14px; - padding-left: 14px; + margin-left: 12px; + padding-left: 12px; } .meta-row > span + span::before { @@ -203,51 +220,61 @@ a { top: 50%; width: 6px; height: 1px; - background: #111111; + background: #7d7d7d; transform: translateY(-50%); } -.post-meta-row > span + span::before { - content: "|"; - width: auto; - height: auto; - background: none; - top: 0; - transform: none; -} - .post { - max-width: 760px; + border-top: 0; } -.post-topline { - margin-bottom: 16px; -} - -.home-link { - text-decoration: none; - border-bottom: 1px solid #111111; +.post-header { + max-width: 100%; + margin: 0; + padding: 24px 24px 0; + border-bottom: 0; } .post-header h1 { margin: 0; - line-height: 1.15; - font-size: clamp(2rem, 6vw, 3.4rem); + line-height: 1.07; + font-size: clamp(2rem, 5vw, 3rem); + font-weight: 500; + letter-spacing: -0.03em; + max-width: 26ch; } .post-header p { - margin: 12px 0; - font-size: 1.06rem; + margin: 14px 0 0; + max-width: 70ch; + font-size: 1rem; + color: #1b1b1b; +} + +.post-meta-row { + margin-top: 14px; +} + +.post-meta-row a { + color: #1f1f1f; } .post-hero { - margin-top: 24px; + max-width: 100%; + margin: 0; + padding: 24px 24px 0; + border-bottom: 0; +} + +.post-hero .post-banner { + width: 100%; + margin: 0; } .article { - margin-top: 32px; - border-top: 1px solid #111111; - padding-top: 24px; + max-width: 100%; + margin: 0; + padding: 30px 24px 52px; } .article > *:first-child { @@ -257,56 +284,92 @@ a { .article h2, .article h3, .article h4 { - margin-top: 1.9em; - line-height: 1.25; + margin-top: 1.6em; + margin-bottom: 0.55em; + line-height: 1.2; + font-weight: 500; +} + +.article p, +.article li, +.article blockquote { + font-size: 1.04rem; +} + +.article ul, +.article ol { + padding-left: 1.3rem; +} + +.article a { + color: var(--accent); } .article pre { - border: 1px solid #111111; + border: 1px solid var(--line); padding: 14px; overflow: auto; - background: #ffffff; + background: #f7f7f7; + font-size: 0.9rem; } .article code { - font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + font-size: 0.9em; +} + +.article pre, +.article kbd, +.article samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; } .article blockquote { - margin-left: 0; - padding-left: 16px; - border-left: 2px solid #111111; + margin: 1.2em 0; + padding: 0 0 0 14px; + border-left: 2px solid var(--line); + color: #292929; } .article img { max-width: 100%; height: auto; + border: 0; } -@media (max-width: 740px) { +@media (max-width: 860px) { .page { - padding: 16px; + width: 100%; + border-left: 0; + border-right: 0; + padding-top: 0; } - .index { - grid-template-columns: 1fr; + .header-row { + min-height: 64px; + padding: 0 16px; + } + + .post-card, + .post-header, + .post-hero, + .article, + .status { + padding-left: 16px; + padding-right: 16px; } .index-grid { grid-template-columns: 1fr; } - .index-grid .post-card:nth-child(n + 3) { - border-top: 0; - padding-top: 0; - } - - .index-grid .post-card + .post-card { - border-top: 1px solid #111111; - padding-top: 18px; + .index-grid .post-card:nth-child(odd) { + border-right: 0; } .post-title { - font-size: 1.45rem; + font-size: 1.38rem; } } diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +///