CSS Audit Design

Goal

Establish clear separation of concerns across HTML, CSS, and JS:

  • Tailwind classes for all styling (no hardcoded inline styles)
  • data-* attributes for JS targeting only (no IDs)
  • CSS classes for custom styling in <style> blocks (not data-attr selectors, not IDs)
  • No IDs anywhere in the codebase
  • No hardcoded style.* in JS — use class toggling instead

Scope

All files in app/src/ except the <live-window> web component (scripts/live-window/), which uses Shadow DOM isolation and runtime-computed styles by design.

Decisions

  • Data attribute convention: Component-namespaced (data-layout, data-nav, data-cp, data-chat, data-login, data-home)
  • Tailwind data variants: Keep as-is (data-own:text-accent, data-admin:text-admin, data-online:bg-neon/70, data-active:*) — this is Tailwind’s intended pattern
  • Scroll locking: Replace document.body.style.overflow with classList.add/remove("overflow-hidden")
  • Login/logout pages: Refactor onto BaseLayout + Tailwind

Section 1: Data Attribute Namespaces & ID Migration

Namespace mapping

ComponentNamespace
BaseLayoutdata-layout
ProjectTree/Navdata-nav
CommandPalettedata-cp
Chatdata-chat
Login pagedata-login
Homepagedata-home

BaseLayout (data-layout)

Old IDNew data attrCSS class
js-toolbar-menu-btndata-layout="menu-btn"
nav-paneldata-layout="nav-panel".layout-nav-panel
nav-backdropdata-layout="nav-backdrop".layout-nav-backdrop
chat-overlaydata-layout="chat-overlay".layout-chat-overlay
chat-overlay-backdropdata-layout="chat-overlay-backdrop".layout-chat-overlay-backdrop
chat-overlay-closedata-layout="chat-overlay-close".layout-chat-overlay-close
js-chat-overlay-contentdata-layout="chat-overlay-content"
js-mobile-chat-fabdata-layout="chat-fab"
toolbar-search-btndata-layout="search-btn"

ProjectTree/Nav (data-nav)

Old IDNew data attrCSS class
site-navdata-nav="root".nav-root
js-nav-search-btndata-nav="search-btn"
js-admin-linkdata-nav="admin-link"

Chat (data-chat)

Old IDNew data attrCSS classNotes
js-chat-paneldata-chat="panel"
js-chat-owner-wrapdata-chat="owner-wrap"
js-chat-status-dotdata-chat="owner-status-dot"Avoids collision with template data-chat="status-dot"
js-chat-owner-statusdata-chat="owner-status"
js-chat-user-countdata-chat="user-count"No collision with template data-chat="viewer-count"
js-chat-messagesdata-chat="messages"
js-chat-username-rowdata-chat="username-row"
chat-usernamedata-chat="username-input".chat-username-inputAvoids collision with template data-chat="username"
js-chat-inputdata-chat="input"
js-chat-senddata-chat="send"
chat-msg-tpldata-chat="msg-tpl"
chat-notice-tpldata-chat="notice-tpl"

CSS selector migration:

CurrentNew
[data-chat="delete-btn"][data-confirm].chat-delete-confirm (JS toggles class)
[data-chat="flag-btn"][data-confirm].chat-flag-confirm (JS toggles class)

CommandPalette (data-cp)

Old IDNew data attrCSS class
command-palettedata-cp="overlay".cp-overlay
js-cp-backdropdata-cp="backdrop"
js-cp-dialogdata-cp="dialog".cp-dialog
js-cp-collection-selectdata-cp="collection-select"
js-cp-inputdata-cp="input"
cp-resultsdata-cp="results".cp-results
cp-row-tpldata-cp="row-tpl"
cp-row-external-tpldata-cp="row-external-tpl"
cp-empty-tpldata-cp="empty-tpl"

Login page (data-login)

Old IDNew data attrCSS class
js-admin-formdata-login="form"
js-admin-secretdata-login="secret"
js-admin-errordata-login="error".login-error

Homepage (data-home)

Old IDNew data attrCSS class
js-greetingdata-home="greeting"

Section 2: Inline Styles → Tailwind

FileCurrentTailwind
index.astro:64style="min-width:max-content"min-w-max
SpotifyPlayer.astro:5style="transform: scale(0.88); transform-origin: bottom left; margin-right: -12%;"scale-[0.88] origin-bottom-left mr-[-12%]
SpotifyPlayer.astro:16style="border-radius:12px;"rounded-xl
SectionHeader.astro:21style="font-size:0.55em"text-[0.55em]
LinkCard.astro:59style="font-size:10px"text-[10px]
CommandPalette.astro:157style="font-size:10px"text-[10px]

Keep as inline style (runtime-computed):

  • CardCover.astro:23 — dynamic background-color/background-image from server-side computation

Section 3: <style> Block CSS Cleanup

BaseLayout.astro <style is:global>

Replace ID selectors with class selectors:

  • #nav-backdrop.visible.layout-nav-backdrop.visible
  • #chat-overlay-backdrop.visible.layout-chat-overlay-backdrop.visible
  • #nav-panel.open.layout-nav-panel.open
  • #chat-overlay.open.layout-chat-overlay.open
  • #chat-overlay #chat-overlay-close.layout-chat-overlay .layout-chat-overlay-close

Convert raw CSS to @apply where Tailwind equivalents exist.

CommandPalette.astro <style>

  • #command-palette.cp-overlay--open.cp-overlay.cp-overlay--open
  • #js-cp-dialog.cp-dialog
  • #cp-results.cp-results
  • .cp-row__img--empty span and .cp-row__initial gradient rules → @apply gradient-text

Chat.astro <style>

  • #chat-username.chat-username-input
  • [data-chat="delete-btn"][data-confirm].chat-delete-confirm
  • [data-chat="flag-btn"][data-confirm].chat-flag-confirm

ProjectTree.astro <style>

  • :global(#nav-panel) #site-nav:global(.layout-nav-panel) .nav-root
  • Convert display: flex; flex-direction: column; padding: 0.75rem@apply flex flex-col p-3

index.astro <style>

  • .home-timeline__name { font-size: 0.65rem }@apply text-2xs
  • .home-timeline__track::before — convert top: 35px; left: 0; right: 0@apply top-[35px] left-0 right-0
  • Keep mask-image rules as-is (no Tailwind equivalent)
  • Keep -webkit-overflow-scrolling: touch as-is (no Tailwind equivalent)

login.astro <style>

  • Replace #js-admin-error.login-error
  • (Entire page refactored onto BaseLayout in Section 5)

[…id].astro (post page) <style>

  • Convert remaining raw CSS to @apply: max-width: 100%@apply max-w-full, opacity: 0@apply opacity-0, etc.

Section 4: JS Hardcoded Styles Cleanup

BaseLayout.ts

Replace document.body.style.overflow with class toggling:

// Before
document.body.style.overflow = "hidden";
document.body.style.overflow = "";
// After
document.body.classList.add("overflow-hidden");
document.body.classList.remove("overflow-hidden");

Replace all getElementById with querySelector('[data-layout="..."]').

CommandPalette.ts

Replace all getElementById with querySelector('[data-cp="..."]').

chat-client.ts

Replace all getElementById with querySelector('[data-chat="..."]').

ProjectTree.ts

Replace getElementById("site-nav") with querySelector('[data-nav="root"]').

login.astro inline script

Replace getElementById calls with querySelector('[data-login="..."]').

index.astro inline script

Replace getElementById("js-greeting") with querySelector('[data-home="greeting"]').


Section 5: Login/Logout Page Refactoring

Refactor both pages to use BaseLayout + Tailwind:

  • Wrap content in <BaseLayout> component
  • Replace all raw CSS with Tailwind utility classes
  • Remove duplicated CSS variable definitions
  • Add --color-error: #ff4444 to theme.css (currently only in login.astro)

Out of Scope

  • <live-window> web component (Shadow DOM isolation, runtime-computed styles)
  • CardCover.astro dynamic inline style (server-computed values)
  • Tailwind data-* variants (data-own:text-accent, etc.) — Tailwind’s intended pattern
  • alerts.css — generated by remark plugin, styles third-party HTML
  • cursors.css — cursor overrides that extend Tailwind’s cursor classes
  • base.css@layer base element resets (correct approach)