From b216a2de60e61d851cb455d83274131e0c31336e Mon Sep 17 00:00:00 2001 From: trash07 Date: Sun, 29 Mar 2026 11:08:46 -0400 Subject: [PATCH 1/3] feat: css layout --- src/components/fragment/Footer.jsx | 41 + src/components/fragment/Header.jsx | 28 + src/components/layout/IndexLayout.jsx | 15 + src/components/{ => layout}/PostLayout.jsx | 0 src/components/seo/MainSeo.jsx | 0 src/components/{ => seo}/PostSeo.jsx | 13 + src/css/main.css | 355 ++++++++ src/css/post.css | 792 ++++++++++++++++++ .../blog-posts/{mdx.frontmatter__slug}.jsx | 4 +- src/pages/index.jsx | 53 +- 10 files changed, 1298 insertions(+), 3 deletions(-) create mode 100644 src/components/fragment/Footer.jsx create mode 100644 src/components/fragment/Header.jsx create mode 100644 src/components/layout/IndexLayout.jsx rename src/components/{ => layout}/PostLayout.jsx (100%) create mode 100644 src/components/seo/MainSeo.jsx rename src/components/{ => seo}/PostSeo.jsx (78%) create mode 100644 src/css/main.css create mode 100644 src/css/post.css diff --git a/src/components/fragment/Footer.jsx b/src/components/fragment/Footer.jsx new file mode 100644 index 0000000..61e5ac5 --- /dev/null +++ b/src/components/fragment/Footer.jsx @@ -0,0 +1,41 @@ +import * as React from 'react'; + +export default function Footer() { + return ( + + ); +} diff --git a/src/components/fragment/Header.jsx b/src/components/fragment/Header.jsx new file mode 100644 index 0000000..fcd659d --- /dev/null +++ b/src/components/fragment/Header.jsx @@ -0,0 +1,28 @@ +import * as React from 'react'; + +export default function Header() { + return ( + + ); +} diff --git a/src/components/layout/IndexLayout.jsx b/src/components/layout/IndexLayout.jsx new file mode 100644 index 0000000..99db862 --- /dev/null +++ b/src/components/layout/IndexLayout.jsx @@ -0,0 +1,15 @@ +import * as React from 'react'; + +import '../../css/main.css'; +import Footer from '../fragment/Footer'; +import Header from '../fragment/Header'; + +export default function IndexLayout({ children }) { + return ( + <> +
+ {children} +
+ + ); +} diff --git a/src/components/PostLayout.jsx b/src/components/layout/PostLayout.jsx similarity index 100% rename from src/components/PostLayout.jsx rename to src/components/layout/PostLayout.jsx diff --git a/src/components/seo/MainSeo.jsx b/src/components/seo/MainSeo.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/PostSeo.jsx b/src/components/seo/PostSeo.jsx similarity index 78% rename from src/components/PostSeo.jsx rename to src/components/seo/PostSeo.jsx index edaff59..5341f7c 100644 --- a/src/components/PostSeo.jsx +++ b/src/components/seo/PostSeo.jsx @@ -34,11 +34,24 @@ export function PostTwitterSeo({ frontmatter }) { ); } +export function PreloadGoogleFonts() { + return ( + <> + + + + ); +} + export function PostSeo({ frontmatter }) { return ( <> + ); } diff --git a/src/css/main.css b/src/css/main.css new file mode 100644 index 0000000..1e18efa --- /dev/null +++ b/src/css/main.css @@ -0,0 +1,355 @@ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --bg: #fafafa; + --surface: #ffffff; + --border: #e8e8e8; + --text: #111111; + --muted: #888888; + --accent: #0066ff; + --accent-soft: #e8f0ff; + --tag1: #eef2ff; + --tag1t: #4f46e5; + --tag2: #f0fdf4; + --tag2t: #16a34a; + --tag3: #fff7ed; + --tag3t: #ea580c; + --radius: 8px; + --font: 'Inter', system-ui, sans-serif; + --max: 860px; +} + +body { + font-family: var(--font); + background: var(--bg); + color: var(--text); + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* ── NAV ── */ +nav { + position: sticky; + top: 0; + background: rgba(250, 250, 250, 0.92); + backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); + z-index: 100; +} +.nav-inner { + max-width: var(--max); + margin: 0 auto; + padding: 0 20px; + height: 56px; + display: flex; + align-items: center; +} +.logo { + font-weight: 700; + font-size: 1rem; + letter-spacing: -0.5px; + color: var(--text); + text-decoration: none; + flex-shrink: 0; +} +.logo span { + color: var(--accent); +} + +.nav-links { + display: flex; + gap: 24px; + margin-left: auto; + align-items: center; +} +.nav-links a { + font-size: 0.875rem; + color: var(--muted); + text-decoration: none; + transition: color 0.15s; +} +.nav-links a:hover { + color: var(--text); +} + +/* Hamburger */ +.hamburger { + display: none; + flex-direction: column; + gap: 5px; + margin-left: auto; + cursor: pointer; + padding: 4px; + background: none; + border: none; +} +.hamburger span { + display: block; + width: 22px; + height: 2px; + background: var(--text); + border-radius: 2px; + transition: + transform 0.25s, + opacity 0.25s; +} +.hamburger.open span:nth-child(1) { + transform: translateY(7px) rotate(45deg); +} +.hamburger.open span:nth-child(2) { + opacity: 0; +} +.hamburger.open span:nth-child(3) { + transform: translateY(-7px) rotate(-45deg); +} + +/* Mobile nav drawer */ +.mobile-menu { + display: none; + flex-direction: column; + background: var(--surface); + border-bottom: 1px solid var(--border); + padding: 8px 20px 16px; + gap: 4px; +} +.mobile-menu.open { + display: flex; +} +.mobile-menu a { + font-size: 0.95rem; + color: var(--text); + text-decoration: none; + padding: 10px 0; + border-bottom: 1px solid var(--border); +} +.mobile-menu a:last-child { + border-bottom: none; +} + +/* ── HERO ── */ +.hero { + max-width: var(--max); + margin: 0 auto; + padding: 64px 20px 48px; + border-bottom: 1px solid var(--border); + width: 100%; +} +.hero-label { + font-size: 0.72rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--accent); + margin-bottom: 14px; +} +.hero h1 { + font-size: clamp(1.8rem, 6vw, 3rem); + font-weight: 800; + letter-spacing: -1.5px; + line-height: 1.1; +} +.hero p { + margin-top: 14px; + font-size: clamp(0.95rem, 2.5vw, 1.05rem); + color: var(--muted); + max-width: 480px; + line-height: 1.6; +} + +/* ── LIST ── */ +.list { + max-width: var(--max); + margin: 0 auto; + padding: 0 20px; + flex: 1; + width: 100%; +} +.tutorial-item { + padding: 24px 0; + border-bottom: 1px solid var(--border); + display: flex; + flex-direction: column; + gap: 10px; +} +.tutorial-item h2 { + font-size: clamp(1rem, 2.5vw, 1.1rem); + font-weight: 700; + letter-spacing: -0.3px; +} +.tutorial-item h2 a { + color: inherit; + text-decoration: none; +} +.tutorial-item h2 a:hover { + color: var(--accent); +} + +.meta { + font-size: 0.78rem; + color: var(--muted); + display: flex; + align-items: center; + gap: 8px; +} +.meta::before { + content: ''; + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--border); + flex-shrink: 0; +} +.tags { + display: flex; + gap: 6px; + flex-wrap: wrap; +} +.tag { + display: inline-flex; + align-items: center; + padding: 3px 10px; + border-radius: 99px; + font-size: 0.7rem; + font-weight: 600; + white-space: nowrap; +} +.tag:nth-child(3n + 1) { + background: var(--tag1); + color: var(--tag1t); +} +.tag:nth-child(3n + 2) { + background: var(--tag2); + color: var(--tag2t); +} +.tag:nth-child(3n + 3) { + background: var(--tag3); + color: var(--tag3t); +} + +/* ── FOOTER ── */ +footer { + background: var(--surface); + border-top: 1px solid var(--border); + margin-top: auto; +} +.footer-inner { + max-width: var(--max); + margin: 0 auto; + padding: 28px 20px; + display: flex; + flex-direction: column; + gap: 20px; +} +.footer-top { + display: flex; + align-items: flex-start; + justify-content: space-between; + flex-wrap: wrap; + gap: 20px; +} +.footer-brand { + display: flex; + flex-direction: column; + gap: 12px; +} +.search-wrap { + display: flex; + gap: 8px; + align-items: center; +} +.search-wrap input { + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 8px 14px; + font-size: 0.85rem; + outline: none; + background: var(--bg); + color: var(--text); + width: 200px; + transition: border-color 0.15s; +} +.search-wrap input:focus { + border-color: var(--accent); +} +.search-wrap button { + background: var(--accent); + color: #fff; + border: none; + padding: 8px 16px; + border-radius: var(--radius); + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + white-space: nowrap; +} +.socials { + display: flex; + gap: 10px; + align-items: center; +} +.social-btn { + width: 36px; + height: 36px; + border-radius: 50%; + border: 1.5px solid var(--border); + display: flex; + align-items: center; + justify-content: center; + color: var(--muted); + cursor: pointer; + transition: + border-color 0.15s, + color 0.15s; +} +.social-btn:hover { + border-color: var(--accent); + color: var(--accent); +} +.social-btn svg { + width: 15px; + height: 15px; + fill: currentColor; +} +.footer-copy { + font-size: 0.72rem; + color: var(--muted); +} + +/* ── RESPONSIVE ── */ +@media (max-width: 600px) { + .nav-links { + display: none; + } + .hamburger { + display: flex; + } + + .hero { + padding: 40px 20px 36px; + } + + .footer-top { + flex-direction: column; + } + .search-wrap input { + width: 100%; + flex: 1; + } + .search-wrap { + width: 100%; + } + .footer-brand { + width: 100%; + } + .footer-top > .socials { + align-self: flex-start; + } +} diff --git a/src/css/post.css b/src/css/post.css new file mode 100644 index 0000000..c2b01ba --- /dev/null +++ b/src/css/post.css @@ -0,0 +1,792 @@ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --bg: #fafafa; + --surface: #ffffff; + --border: #e8e8e8; + --text: #111111; + --muted: #888888; + --accent: #0066ff; + --accent-soft: #e8f0ff; + --tag1: #eef2ff; + --tag1t: #4f46e5; + --tag2: #f0fdf4; + --tag2t: #16a34a; + --tag3: #fff7ed; + --tag3t: #ea580c; + --code-bg: #f4f4f5; + --code-block-bg: #18181b; + --code-text: #e4e4e7; + --radius: 8px; + --font: 'Inter', system-ui, sans-serif; + --font-mono: 'Fira Code', 'Cascadia Code', monospace; + --max: 860px; +} + +body { + font-family: var(--font); + background: var(--bg); + color: var(--text); + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* ── NAV ── */ +nav { + position: sticky; + top: 0; + background: rgba(250, 250, 250, 0.92); + backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); + z-index: 100; +} +.nav-inner { + max-width: var(--max); + margin: 0 auto; + padding: 0 20px; + height: 56px; + display: flex; + align-items: center; +} +.logo { + font-weight: 700; + font-size: 1rem; + letter-spacing: -0.5px; + color: var(--text); + text-decoration: none; + flex-shrink: 0; +} +.logo span { + color: var(--accent); +} +.nav-links { + display: flex; + gap: 24px; + margin-left: auto; + align-items: center; +} +.nav-links a { + font-size: 0.875rem; + color: var(--muted); + text-decoration: none; + transition: color 0.15s; +} +.nav-links a:hover { + color: var(--text); +} + +.hamburger { + display: none; + flex-direction: column; + gap: 5px; + margin-left: auto; + cursor: pointer; + padding: 4px; + background: none; + border: none; +} +.hamburger span { + display: block; + width: 22px; + height: 2px; + background: var(--text); + border-radius: 2px; + transition: + transform 0.25s, + opacity 0.25s; +} +.hamburger.open span:nth-child(1) { + transform: translateY(7px) rotate(45deg); +} +.hamburger.open span:nth-child(2) { + opacity: 0; +} +.hamburger.open span:nth-child(3) { + transform: translateY(-7px) rotate(-45deg); +} +.mobile-menu { + display: none; + flex-direction: column; + background: var(--surface); + border-bottom: 1px solid var(--border); + padding: 8px 20px 16px; + gap: 4px; +} +.mobile-menu.open { + display: flex; +} +.mobile-menu a { + font-size: 0.95rem; + color: var(--text); + text-decoration: none; + padding: 10px 0; + border-bottom: 1px solid var(--border); +} +.mobile-menu a:last-child { + border-bottom: none; +} + +/* ── BREADCRUMB ── */ +.breadcrumb-bar { + border-bottom: 1px solid var(--border); + background: var(--surface); +} +.breadcrumb { + max-width: var(--max); + margin: 0 auto; + padding: 12px 20px; + font-size: 0.78rem; + color: var(--muted); + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} +.breadcrumb a { + color: var(--muted); + text-decoration: none; +} +.breadcrumb a:hover { + color: var(--accent); +} +.breadcrumb .sep { + color: var(--border); +} +.breadcrumb .current { + color: var(--text); + font-weight: 500; +} + +/* ── PAGE LAYOUT ── */ +.page { + max-width: var(--max); + margin: 0 auto; + padding: 0 20px; + flex: 1; + width: 100%; + display: grid; + grid-template-columns: 1fr 220px; + gap: 48px; + align-items: start; + padding-top: 48px; + padding-bottom: 64px; +} + +/* ── ARTICLE ── */ +article { + min-width: 0; + overflow-x: hidden; +} + +.post-header { + margin-bottom: 36px; +} + +.post-tags { + display: flex; + gap: 6px; + flex-wrap: wrap; + margin-bottom: 16px; +} +.tag { + display: inline-flex; + align-items: center; + padding: 3px 10px; + border-radius: 99px; + font-size: 0.7rem; + font-weight: 600; + white-space: nowrap; +} +.tag:nth-child(3n + 1) { + background: var(--tag1); + color: var(--tag1t); +} +.tag:nth-child(3n + 2) { + background: var(--tag2); + color: var(--tag2t); +} +.tag:nth-child(3n + 3) { + background: var(--tag3); + color: var(--tag3t); +} + +.post-title { + font-size: clamp(1.6rem, 5vw, 2.2rem); + font-weight: 800; + letter-spacing: -1px; + line-height: 1.15; + margin-bottom: 16px; +} + +.post-meta { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; + font-size: 0.8rem; + color: var(--muted); + padding-bottom: 24px; + border-bottom: 1px solid var(--border); +} +.post-meta .dot { + width: 3px; + height: 3px; + border-radius: 50%; + background: var(--border); +} +.post-meta .author { + display: flex; + align-items: center; + gap: 8px; +} +.avatar { + width: 24px; + height: 24px; + border-radius: 50%; + background: linear-gradient(135deg, var(--accent), #6366f1); + display: flex; + align-items: center; + justify-content: center; + font-size: 0.65rem; + font-weight: 700; + color: #fff; + flex-shrink: 0; +} + +/* ── ARTICLE BODY ── */ +.prose { + line-height: 1.75; + font-size: 1rem; + color: #222; +} +.prose > * + * { + margin-top: 1.4em; +} + +.prose h2 { + font-size: 1.3rem; + font-weight: 700; + letter-spacing: -0.4px; + margin-top: 2.2em; + margin-bottom: 0.6em; + padding-top: 0.4em; + border-top: 1px solid var(--border); + scroll-margin-top: 72px; +} +.prose h3 { + font-size: 1.05rem; + font-weight: 700; + margin-top: 1.8em; + margin-bottom: 0.4em; + scroll-margin-top: 72px; +} + +.prose p { + color: #333; +} + +.prose a { + color: var(--accent); + text-decoration: underline; + text-decoration-color: #0066ff44; +} +.prose a:hover { + text-decoration-color: var(--accent); +} + +.prose code { + font-family: var(--font-mono); + font-size: 0.85em; + background: var(--code-bg); + color: #c026d3; + padding: 2px 6px; + border-radius: 4px; +} + +.prose pre { + background: var(--code-block-bg); + border-radius: var(--radius); + overflow-x: auto; + -webkit-overflow-scrolling: touch; + padding: 0; + margin: 1.6em 0; + /* Prevent the pre from ever forcing the page wider */ + max-width: 100%; +} +.code-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 16px; + border-bottom: 1px solid #2a2a2a; +} +.code-lang { + font-family: var(--font-mono); + font-size: 0.7rem; + color: #71717a; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.copy-btn { + font-size: 0.7rem; + color: #71717a; + background: none; + border: 1px solid #3f3f46; + border-radius: 4px; + padding: 3px 10px; + cursor: pointer; + font-family: var(--font); + transition: + color 0.15s, + border-color 0.15s; +} +.copy-btn:hover { + color: #e4e4e7; + border-color: #71717a; +} +.prose pre code { + display: block; + font-family: var(--font-mono); + font-size: 0.855rem; + color: var(--code-text); + background: none; + padding: 16px; + line-height: 1.7; + tab-size: 2; +} +/* Syntax highlight colours */ +.kw { + color: #c084fc; +} /* keyword */ +.fn { + color: #60a5fa; +} /* function */ +.str { + color: #86efac; +} /* string */ +.cm { + color: #52525b; + font-style: italic; +} /* comment */ +.nm { + color: #fbbf24; +} /* name/variable */ +.op { + color: #94a3b8; +} /* operator */ + +.prose ul, +.prose ol { + padding-left: 1.5em; + color: #333; +} +.prose li + li { + margin-top: 0.4em; +} + +.prose blockquote { + border-left: 3px solid var(--accent); + padding: 12px 18px; + background: var(--accent-soft); + border-radius: 0 var(--radius) var(--radius) 0; + color: #444; + font-size: 0.95em; +} + +/* ── SIDEBAR ── */ +.sidebar { + position: sticky; + top: 72px; +} +.toc { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 18px 20px; +} +.toc-title { + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--muted); + margin-bottom: 12px; +} +.toc ol { + list-style: none; + padding: 0; + display: flex; + flex-direction: column; + gap: 2px; +} +.toc li a { + font-size: 0.8rem; + color: var(--muted); + text-decoration: none; + display: block; + padding: 5px 8px; + border-radius: 4px; + transition: + background 0.12s, + color 0.12s; + line-height: 1.4; +} +.toc li a:hover { + background: var(--bg); + color: var(--text); +} +.toc li a.active { + color: var(--accent); + background: var(--accent-soft); + font-weight: 600; +} +.toc li.sub a { + padding-left: 18px; + font-size: 0.75rem; +} + +/* Mobile TOC toggle */ +.toc-mobile { + display: none; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); + margin-bottom: 28px; + overflow: hidden; +} +.toc-toggle { + width: 100%; + background: none; + border: none; + padding: 14px 16px; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.85rem; + font-weight: 600; + color: var(--text); + cursor: pointer; + font-family: var(--font); +} +.toc-toggle svg { + width: 16px; + height: 16px; + fill: var(--muted); + transition: transform 0.2s; +} +.toc-toggle.open svg { + transform: rotate(180deg); +} +.toc-mobile-body { + display: none; + border-top: 1px solid var(--border); + padding: 8px 0; +} +.toc-mobile-body.open { + display: block; +} +.toc-mobile-body ol { + list-style: none; + padding: 0; +} +.toc-mobile-body a { + font-size: 0.85rem; + color: var(--muted); + text-decoration: none; + display: block; + padding: 8px 16px; +} +.toc-mobile-body a:hover { + color: var(--accent); +} +.toc-mobile-body .sub a { + padding-left: 28px; + font-size: 0.8rem; +} + +/* ── RELATED ── */ +.related { + max-width: var(--max); + margin: 0 auto; + padding: 40px 20px 56px; + width: 100%; + border-top: 1px solid var(--border); +} +.related-title { + font-size: 0.72rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--muted); + margin-bottom: 20px; +} +.related-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; +} +.related-card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 18px 20px; + text-decoration: none; + color: inherit; + display: flex; + flex-direction: column; + gap: 8px; + transition: + border-color 0.15s, + box-shadow 0.15s; +} +.related-card:hover { + border-color: #ccc; + box-shadow: 0 4px 16px #0001; +} +.related-card h3 { + font-size: 0.95rem; + font-weight: 700; + color: var(--text); + line-height: 1.35; +} +.related-card .meta { + font-size: 0.75rem; + color: var(--muted); + display: flex; + align-items: center; + gap: 6px; +} +.related-card .meta::before { + content: ''; + display: inline-block; + width: 5px; + height: 5px; + border-radius: 50%; + background: var(--border); +} + +/* ── FOOTER ── */ +footer { + background: var(--surface); + border-top: 1px solid var(--border); +} +.footer-inner { + max-width: var(--max); + margin: 0 auto; + padding: 28px 20px; + display: flex; + flex-direction: column; + gap: 20px; +} +.footer-top { + display: flex; + align-items: flex-start; + justify-content: space-between; + flex-wrap: wrap; + gap: 20px; +} +.footer-brand { + display: flex; + flex-direction: column; + gap: 12px; +} +.search-wrap { + display: flex; + gap: 8px; + align-items: center; +} +.search-wrap input { + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 8px 14px; + font-size: 0.85rem; + outline: none; + background: var(--bg); + color: var(--text); + width: 200px; + transition: border-color 0.15s; +} +.search-wrap input:focus { + border-color: var(--accent); +} +.search-wrap button { + background: var(--accent); + color: #fff; + border: none; + padding: 8px 16px; + border-radius: var(--radius); + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; +} +.socials { + display: flex; + gap: 10px; + align-items: center; +} +.social-btn { + width: 36px; + height: 36px; + border-radius: 50%; + border: 1.5px solid var(--border); + display: flex; + align-items: center; + justify-content: center; + color: var(--muted); + cursor: pointer; + transition: + border-color 0.15s, + color 0.15s; +} +.social-btn:hover { + border-color: var(--accent); + color: var(--accent); +} +.social-btn svg { + width: 15px; + height: 15px; + fill: currentColor; +} +.footer-copy { + font-size: 0.72rem; + color: var(--muted); +} + +/* ── RESPONSIVE ── */ + +/* Tablet — sidebar too narrow, collapse it early */ +@media (max-width: 780px) { + .page { + grid-template-columns: 1fr; + gap: 0; + padding-top: 32px; + padding-bottom: 48px; + } + .sidebar { + display: none; + } + .toc-mobile { + display: block; + } +} + +/* Mobile */ +@media (max-width: 600px) { + .nav-links { + display: none; + } + .hamburger { + display: flex; + } + + /* Breadcrumb: truncate the long current-page label */ + .breadcrumb { + flex-wrap: nowrap; + overflow: hidden; + } + .breadcrumb .current { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; + } + .breadcrumb .sep:last-of-type + .current { + display: block; + } + + /* Post header */ + .post-header { + margin-bottom: 24px; + } + .post-title { + letter-spacing: -0.6px; + } + + /* Post meta: hide separator dots, let items wrap naturally */ + .post-meta { + gap: 8px 14px; + } + .post-meta .dot { + display: none; + } + + /* Prose */ + .prose { + font-size: 0.97rem; + } + .prose h2 { + font-size: 1.15rem; + margin-top: 1.8em; + } + .prose h3 { + font-size: 1rem; + } + + /* Code blocks: smaller font, smooth touch scroll */ + .prose pre { + border-radius: 6px; + /* Extend to edge on very small screens */ + margin-left: -20px; + margin-right: -20px; + border-radius: 0; + } + .prose pre code { + font-size: 0.78rem; + padding: 14px 20px; + -webkit-overflow-scrolling: touch; + } + .code-header { + padding: 8px 20px; + } + + /* Blockquote */ + .prose blockquote { + padding: 10px 14px; + } + + /* Page padding tighter */ + .page { + padding-top: 20px; + padding-bottom: 32px; + } + + /* Related */ + .related { + padding: 28px 20px 40px; + } + .related-grid { + grid-template-columns: 1fr; + } + + /* Footer */ + .footer-top { + flex-direction: column; + } + .search-wrap { + width: 100%; + } + .search-wrap input { + flex: 1; + width: auto; + } + .footer-brand { + width: 100%; + } +} + +/* Extra small — very narrow phones */ +@media (max-width: 380px) { + .prose pre code { + font-size: 0.72rem; + } + .post-title { + font-size: 1.45rem; + } +} diff --git a/src/pages/blog-posts/{mdx.frontmatter__slug}.jsx b/src/pages/blog-posts/{mdx.frontmatter__slug}.jsx index efbbbb2..6c4c4f0 100644 --- a/src/pages/blog-posts/{mdx.frontmatter__slug}.jsx +++ b/src/pages/blog-posts/{mdx.frontmatter__slug}.jsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { PostLayout } from '../../components/PostLayout'; -import { PostSeo } from '../../components/PostSeo'; +import { PostLayout } from '../../components/layout/PostLayout'; +import { PostSeo } from '../../components/seo/PostSeo'; export function Head({ pageContext }) { const { frontmatter } = pageContext; diff --git a/src/pages/index.jsx b/src/pages/index.jsx index ac0a862..2d35f90 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -1,7 +1,58 @@ import * as React from 'react'; +import IndexLayout from '../components/layout/IndexLayout'; const IndexPage = () => { - return <>Home Page; + return ( + +
+
Learn by doing
+

+ Dev tutorials, +
+ straight to the point. +

+

+ Practical guides on web development, tools, and best practices — + written for developers who value their time. +

+
+ +
+
+

+ Building a REST API with Node.js and Express +

+
Published at March 18, 2026
+
+ Node.js + Express + REST API +
+
+ +
+

+ Getting started with TypeScript generics +

+
Published at March 10, 2026
+
+ TypeScript + JavaScript +
+
+ +
+

+ Docker for front-end developers +

+
Published at February 28, 2026
+
+ Docker +
+
+
+
+ ); }; export default IndexPage; From e80fe47b06d54b33e084e25e13ca2ef8c8f1ad76 Mon Sep 17 00:00:00 2001 From: trash07 Date: Sun, 29 Mar 2026 12:23:50 -0400 Subject: [PATCH 2/3] feat: update main page --- ...exposition-application-locale-internet.mdx | 3 + .../installer-jenkins-docker-compose.mdx | 2 + mdx/general/monolitique-microservices.mdx | 3 + src/components/fragment/Footer.jsx | 2 +- src/components/utils/url-generator.jsx | 3 + src/pages/index.jsx | 93 +++++++++++-------- 6 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 src/components/utils/url-generator.jsx diff --git a/mdx/general/exposition-application-locale-internet.mdx b/mdx/general/exposition-application-locale-internet.mdx index 65ae017..1122bbf 100644 --- a/mdx/general/exposition-application-locale-internet.mdx +++ b/mdx/general/exposition-application-locale-internet.mdx @@ -3,6 +3,9 @@ title: Comment exposer une application locale sur internet ? slug: comment-exposer-une-application-locale-sur-internet date: 2022-09-30 author: lkpeto +tags: + - nodejs + - localtunnel --- Des fois, en développant les applications, diff --git a/mdx/general/installer-jenkins-docker-compose.mdx b/mdx/general/installer-jenkins-docker-compose.mdx index 147446b..16c85a9 100644 --- a/mdx/general/installer-jenkins-docker-compose.mdx +++ b/mdx/general/installer-jenkins-docker-compose.mdx @@ -3,6 +3,8 @@ title: Installer Jenkins avec docker compose slug: installer-jenkins-avec-docker-compose date: 2022-10-01 author: lkpeto +tags: + - jenkins --- Dans ce tutoriel, nous allons voir comment installer diff --git a/mdx/general/monolitique-microservices.mdx b/mdx/general/monolitique-microservices.mdx index 505fbf0..1c6a0fe 100644 --- a/mdx/general/monolitique-microservices.mdx +++ b/mdx/general/monolitique-microservices.mdx @@ -3,6 +3,9 @@ title: Du monolitique aux microservices slug: du-monolitique-aux-microservices date: 2022-09-27 author: lkpeto +tags: + - monolitique + - microservices --- Aujourd'hui, le conteneurs nous ont envahi. Dans toutes les applications diff --git a/src/components/fragment/Footer.jsx b/src/components/fragment/Footer.jsx index 61e5ac5..0d53a99 100644 --- a/src/components/fragment/Footer.jsx +++ b/src/components/fragment/Footer.jsx @@ -33,7 +33,7 @@ export default function Footer() {
- © 2026 tutoriel.dev — All rights reserved. + © 2026 tutoriel.dev — All rights reserved.
diff --git a/src/components/utils/url-generator.jsx b/src/components/utils/url-generator.jsx new file mode 100644 index 0000000..85e4974 --- /dev/null +++ b/src/components/utils/url-generator.jsx @@ -0,0 +1,3 @@ +export function generateBlogPostUrl(slug) { + return `blog-posts/${slug}`; +} diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 2d35f90..71af5c5 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -1,60 +1,73 @@ import * as React from 'react'; import IndexLayout from '../components/layout/IndexLayout'; +import { graphql, Link } from 'gatsby'; +import { generateBlogPostUrl } from '../components/utils/url-generator'; -const IndexPage = () => { +const IndexPage = ({ data }) => { + const posts = data?.allMdx?.nodes; return (
-
Learn by doing
+
Share understanding

- Dev tutorials, + Partager la
- straight to the point. + compréhension de sujets

- Practical guides on web development, tools, and best practices — - written for developers who value their time. + Jardin digital en construction pour partager des idees et reflexions + techniques. — Je publie de temps en temps quand libre. Contactez-moi + pour poster et animer ce site communautaire pour tous.

-
-

- Building a REST API with Node.js and Express -

-
Published at March 18, 2026
-
- Node.js - Express - REST API -
-
- -
-

- Getting started with TypeScript generics -

-
Published at March 10, 2026
-
- TypeScript - JavaScript -
-
- -
-

- Docker for front-end developers -

-
Published at February 28, 2026
-
- Docker -
-
+ {posts.map((post, index) => ( + + ))}
); }; -export default IndexPage; +function PostDatails(post) { + const { frontmatter } = post.pos; + + return ( +
+

+ + {frontmatter.title} + +

+
Publié le {frontmatter.dateFormatted}
+
+ {frontmatter.tags.map((tag, index) => ( + + {tag} + + ))} +
+
+ ); +} + +export const Head = () => Bienvenue - tutoriel.dev; -export const Head = () => Home Page; +export const query = graphql` + query LatestPosts($limit: Int = 10) { + allMdx(sort: { frontmatter: { date: DESC } }, limit: $limit) { + nodes { + frontmatter { + title + date + dateFormatted: date(formatString: "D MMMM, YYYY", locale: "fr") + tags + slug + } + } + } + } +`; + +export default IndexPage; From a153157277c028d72c4bbb176639fda9e72c8ab0 Mon Sep 17 00:00:00 2001 From: trash07 Date: Sun, 29 Mar 2026 12:38:54 -0400 Subject: [PATCH 3/3] fix: site url --- src/components/fragment/Footer.jsx | 5 +++-- src/components/utils/url-generator.jsx | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/fragment/Footer.jsx b/src/components/fragment/Footer.jsx index 0d53a99..40d1a55 100644 --- a/src/components/fragment/Footer.jsx +++ b/src/components/fragment/Footer.jsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { getSiteUrl } from '../utils/url-generator'; export default function Footer() { return ( @@ -6,11 +7,11 @@ export default function Footer() {
diff --git a/src/components/utils/url-generator.jsx b/src/components/utils/url-generator.jsx index 85e4974..d39f137 100644 --- a/src/components/utils/url-generator.jsx +++ b/src/components/utils/url-generator.jsx @@ -1,3 +1,7 @@ export function generateBlogPostUrl(slug) { return `blog-posts/${slug}`; } + +export function getSiteUrl() { + return `https://tutoriel.dev`; +}