Files
cmkiss/demo/css/reset.css
T
biosiasandClaude Sonnet 5 24f9740d97 Fix bugs and robustness gaps found in a full code review (AI)
Real bugs:
- loadArticle()/loadAuthorView() had no stale-render guard (unlike
  loadArticleList()/loadSearch()) -- a slower, superseded request could
  overwrite a newer one's render. Both now capture and check a
  generation snapshot.
- #post/... links interpolated article.hash (raw, unencoded
  "author/slug") directly, but router.js unconditionally
  decodeURIComponent()s each segment when parsing -- would throw
  (silently swallowed) for any author/slug containing a literal "%" or
  other reserved character. Links now go through a new postHref()
  helper that encodes each segment individually.
- A startup failure replaced the entire document.body, which would
  wipe out a host site's own page content when cmjs is embedded
  alongside it. Now scoped to cmjs's own mount points when at least
  one exists.
- hydrateArticle() had no in-flight de-duplication -- two concurrent
  callers for the same not-yet-hydrated stub (e.g. a page's hydration
  racing the next page's prefetch) could each fire a duplicate fetch.
  Now tracked via a WeakMap keyed by the stub.
- Leftover "expedition blog"/"expedition year" wording missed by the
  earlier astronomy-club genericization pass (that pass's grep didn't
  cover the English word "expedition").

Robustness gaps:
- cache.js no longer permanently disables the whole IndexedDB cache
  after a single failed transaction -- only openDb() itself failing
  does that now.
- Listing mtimes are normalized to minute precision at the one point
  they enter the system, so cache comparisons stay consistent even if
  a server flips between JSON and HTML-fallback autoindex.
- The root author-listing fetch failing outright is now treated like
  "every author's listing failed" (a new rootFailed crawl stat) instead
  of surfacing a generic, less-specific error.
- router.js now checks both required mount points exist right after
  startup and fails with a clear, catchable message instead of a bare
  TypeError the first time showListView()/showArticleView() runs.
- The article view's "back" link now returns to whatever list/search/
  filter page the user actually came from (tracked via lastListHash)
  instead of always resetting to page 1 of everything.
- filterLabel() now checks filter.type === 'category' explicitly
  instead of falling through to it as the default case.
- Author-name filtering is now case-sensitive, matching how author
  paths/profile lookups are looked up everywhere else.
- Documented (in code comments and a new README note) that article/bio
  Markdown renders via marked.js with no HTML-sanitization pass --
  only trusted authors should be adding content.

Minor:
- Extracted applyArticleMetaFields() to share the date/reading-time/
  category-badge logic between renderCard() and loadArticle() instead
  of duplicating it.
- Clarified resolveMediaUrl()'s WordPress-migration special case as
  legacy compatibility, not a general-purpose feature.
- frontmatter.js no longer requires a trailing newline after the
  closing "---" -- a file that's nothing but frontmatter now parses
  instead of falling through to "no frontmatter at all".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXSVPS3UmmASiiprPfcnUd
2026-09-12 16:23:26 +02:00

76 lines
2.2 KiB
CSS

/* CSS custom properties — single source of truth for colours, radius, etc.
Override these in a downstream stylesheet to theme the whole app. */
:root {
--color-bg: #f7f7f7;
--color-surface: #fff;
--color-border: #e0e0e0;
--color-text: #222;
--color-text-soft: #555;
--color-text-muted: #999;
--color-accent: #0057b8;
--color-tag-bg: #f0f0f0;
--color-tag-border: #ddd;
--color-code-bg: #f4f4f4;
--color-error: #c0392b;
--radius-sm: 3px;
--radius: 6px;
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.08);
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #1a1a1a;
--color-surface: #252525;
--color-border: #3a3a3a;
--color-text: #e0e0e0;
--color-text-soft: #aaa;
--color-text-muted: #666;
--color-accent: #4d9cf0;
--color-tag-bg: #2d2d2d;
--color-tag-border: #3a3a3a;
--color-code-bg: #2a2a2a;
--color-error: #e74c3c;
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.35);
}
}
/* Box-sizing reset and base typography */
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
font-size: 1rem;
line-height: 1.65;
color: var(--color-text);
background: var(--color-bg);
margin: 0;
padding: 0;
}
/* The demo blog follows its own restrained night-sky palette,
independently of the visitor's operating-system theme. */
body.blog-page {
--color-bg: #050506;
--color-surface: #101114;
--color-border: #292b31;
--color-text: #f4f1ed;
--color-text-soft: #c6c4c2;
--color-text-muted: #92959d;
--color-accent: #e25555;
--color-tag-bg: #191b21;
--color-tag-border: #363941;
--color-code-bg: #131519;
--color-error: #ff7777;
--shadow-card: 0 12px 35px rgba(0, 0, 0, 0.28);
}
/* Global keyboard-navigation focus indicator */
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
border-radius: 2px;
}