Skip to content
Agentmart
Submit a tool

useless

What building realness taught us about the web platform - semantic HTML with microdata as one decision that serves SEO, CSS selectors, layout, the data model…

Skill by scott-fryxell

IndexedMIT★ 125
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/scott-fryxell-useless

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

What building realness taught us about the web platform - semantic HTML with microdata as one decision that serves SEO, CSS selectors, layout, the data model, state (no Pinia), file storage paths, and the NoSQL database. Plus baseline-grid spacing, fluid type scale, materials-and-roles color, OS dark mode, and cascade over scoped styles. Use when writing or reviewing CSS/markup/Vue components, cleaning up div soup, or implementing type/spacing/color systems. Defer animation to motion-systems; client type voice to typography.

Use cases: design, frontend, backend, data

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/useless

What's inside

Skill name
useless
Description
What building realness taught us about the web platform - semantic HTML with microdata as one decision that serves SEO, CSS selectors, layout, the data model, state (no Pinia), file storage paths, and the NoSQL database. Plus baseline-grid spacing, fluid type scale, materials-and-roles color, OS dark mode, and cascade over scoped styles. Use when writing or reviewing CSS/markup/Vue components, cleaning up div soup, or implementing type/spacing/color systems. Defer animation to motion-systems; client type voice to typography.
License
MIT
Metadata
category: Design & Frontend
Instructions
276 lines in SKILL.md · view on GitHub

Files (8)

  • SKILL.md
  • BASELINE_STARTER.css · references/
  • COLOR_RECIPE.md · references/
  • TYPE_SCALE_RECIPE.md · references/
  • gate-vs-limit.md · references/examples/
  • vertical-rhythm-cleanup.md · references/examples/
  • markup.md · references/
  • realness-instance.md · references/

SKILL.md (first lines)

# Useless

Semantic HTML first. Style the platform. Not components - elements.

**One markup decision pays seven times.** Microdata is not an SEO garnish bolted
on at the end. `itemid`, `itemprop` and `itemtype` are the same identifiers the
stylesheet selects on, the layout reads with `:has()`, the data layer parses,
the app holds document state in instead of a store, the storage path is built
from, and the database lists as collections. Write the element once and every
layer downstream already knows what it means.

| Layer | What reads the markup |
| ------------------- | ------------------------------------------------------------------------------------------------- |
| SEO | `itemtype` / `itemprop` - Schema.org, read by crawlers and any other reader |
| Design | the attribute is the selector - `address[itemscope]`, `time` |
| Layout | `:has()` on something real - `figure:has([itemtype='/posters'])`, no wrapper invented to hold it |
| Data model | `itemprop_value()` reads `<time datetime>`, `<img src>`, `<a href>` - the element says how to read it |
| Store replacement | no Pinia, no Vuex. `get_item(elements, itemid)` queries the rendered DOM for a document |
| File storage | `itemid` **is** the path - `/+author/posters/1712000000000` becomes `people/+author/posters/1712000000000.html.gz` |
| NoSQL database | `Directory` lists a folder as a collection, cached in idb. Storage is the database; the HTML is the row |

That is the whole argument. Everything below is what follows from taking it
seriously, and the cost of not: an invented class is a name that only one layer
understands, so the other six have to be told separately.

This skill is a method, demonstrated through one project's answer to it. That project is realness (`work/realness`): a rotoscoping tool that traces photos into layered SVG posters. Sections pair a general rule with a "realness's instance" callout. When you bring this to a different project, keep the method and re-derive the specifics - a client's unit, ratio, and palette are not this project's.

## Semantic selectors, not invented ones

**Method**: write every CSS selector against something real - an element, native state, microdata, or ARIA. Never invent a name (a class, or a `data-*` that exists only to be a selector) for something that already has an honest attribute. `<address>` for contact info, `<time>` for dates, `<figure>` for media - the browser ships a rich vocabulary; reach for it before reaching for a selector.

- **Real** = something a human, crawler, or the data layer (`get_item`, Schema) should already recognize.
- **Anonymous** = a node with no meaning outside one widget's internals (spinner spoke, measurement ghost).

Walk this list and stop at the first honest fit:

1. Wrong element? Fix the tag.
2. Native state exists? Use it - `[open]`, `:checked`, `:invalid`, `[hidden]`, `[disabled]`, `:has(dialog[open])`.
3. Meaning for data already needs microdata? Style that - `[itemprop]`, `[itemscope]`, `[itemid]`, `[itemtype]`.
4. Meaning for a11y already needs ARIA? Style that - `[aria-*]`, `[role]`.
5. Global mode, not an element? One attribute on a high node (`html[data-aspect-ratio='16/9']`) - not a class or a `data-*` tree on every region.
6. Still anonymous chrome? A class is allowed - see below.

Microdata and ARIA aren't accessibility/SEO add-ons bolted on afterward - they're load-bearing CSS selectors here, so adding them is never wasted work. If you reach for a class or a new `data-*` for a **region or product thing** (page shell, poster, days feed, delete button), you're reaching for the wrong element or missing an existing attribute - prefer `id` + landmark, parent/child structure, `figure:has(svg[itemtype='/posters'])`, `[itemid]`, or an accessible name instead.

**A `data-*` attribute is a class with different punctuation** unless it earns its keep:

| Earns its keep                                                           | Does not                                                                        |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| Real app/document state CSS must read (`html[data-aspect-ratio='16/9']`) | Renaming a section so you can style it (`data-page` ~ `.page`)                  |
| Bridging JS <-> CSS for boolean state with no native home                  | Product nouns invented for CSS (`data-poster`, `data-days`)                     |
|                                                                          | Widget catalogs that should be element + accessible name (`data-icon='camera'`) |

**A class is allowed only when all of these hold:**

- The node isn't a meaningful element (spinner chrome, measurement ghost, layout scrap with no schema).
- No native / ARIA / microdata attribute honestly fits.
- The name is local to one component's `<style>` block, not shared app vocabulary.
- You'd be ashamed to put that same string on `data-*` for the whole app.

If allowed: BEM-flavored (`block__part`, `is-state`), scoped to that one component - never a bare reusable name like `.card`.

This is the whole system: element, selector, and microdata attribute converge on the same thing.

```css
address[itemscope] {
  font-style: normal;
}
time {
  font-weight: 300;
  display: block;
}
```

Correct semantics reduce the selectors you need; microdata gives you structural hooks without extra classes; element-based CSS rewards using the right element. When you style `time` properly, you use `<time>` properly. For the markup side - element choice, headings, forms, lists vs article - see [references/markup.md](references/markup.md).

realness's instance - the two live carve-outs (exceptions, not patterns to extend): `preference.vue`'s `.compact` and `working-border.vue`'s `.working-border`. Before adding a third, run the decision order above first. Known drift to remove when touching files: [references/realness-instance.md](references/realness-instance.md).

## No invented wrappers

**Method**: a node that exists only so CSS (or JS) can target "this group" is usually the wrong shape. Prefer no wrapper, a real element, or the attribute you already need for meaning - not `<div data-thing>` / `<div class="thing">`.

When you reach for a wrapper, ask what job it does and stop at the first honest fit:

1. **Only one meaningful child?** Delete the wrapper. Style/select the child (`textarea#wat`, `figure:has([itemtype='/posters'])`).
2. **Two siblings that must stay adjacent for CSS** (`nav + section`)? Vue multi-root - no shell. realness: `support-layout.vue` is just `<site-nav />` + `<router-view />`.
3. **Grouping form controls?** Use `<fieldset>` (or `<form>` when submit is the action). Style `fieldset:has(> #wat)`, not `data-posting-input`.
4. **ARIA / landmark role belonging to the region?** Put the role on the region you already have - `section[data-days][role='feed']` - not a child `<div role="feed">` whose only job is the role.
5. **Parent needs to reflect child meaning?** Prefer `:has()` on something real (`figure:has([itemtype='/posters'])`) over inventing `data-poster` on the parent. Keep JS `closest` / `querySelector` on the same hooks; leaving `closest('figure.poster')` after deleting `.poster` is a silent break.
6. **Still need a host with no honest element?** Only then: local class for anonymous chrome (see class carve-out above) - never a shared `data-*` product noun.

| Smell                                               | Prefer                                                                                                       |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `<div data-support-layout>` around nav + view       | Multi-root fragment                                                                                          |
| `<div data-posting-input>` around textarea + button | `<fieldset>` / `<form>`                                                                                      |
| `<div role="feed">` around day articles             | `role="feed"` on the existing section                                                                        |
| `figure[data-poster]` / `.poster`                   | `figure:has([itemtype='/posters'])` (processing figure may carry `itemtype` on itself when no child SVG yet) |
| `data-*` renamed from a former class                | Re-run the selector decision order - punctuation is not progress                                             |

If a wrapper stays, its **element** must earn the keep (fieldset, form, dialog, nav). An attribute hung on a meaningless `div` so the stylesheet can see it does not.

## Architecture

**Method**: organize by HTML element, not by component or feature. One file per element (`a`, `article`, `main`, `nav`, `section`). The browser's element model **is** the architecture. Components sit on top of this, importing element styles and adding page-specific customizations where needed.

Two things sit outside that rule, and belong there rather than being forced into an element file:

- **A foundation file** for whatever every element depends on - a spacing unit, a reset, the type scale. It isn't about one element; it's the substrate the whole system builds on, so it's the one file/module every other file can assume is already loaded.
- **Breakpoint-driven files**. CSS custom properties cannot be read inside `@media` conditions in any browser today - that's a platform limitation, not a tooling gap. Any element whose styling depends on a derived viewport threshold (not just a literal you're willing to hardcode) needs a preprocessing step to compute that threshold at build time. An element file with a real breakpoint stays in whatever preprocessor you're using; one with none can be plain CSS.

realness's instance: a Stylus design system under `src/style/`, one file per element under `elements/`. The foundation file is `base-line.styl`. Stylus's job is deliberately narrow - mixins and breakpoint math, nothing else; files needing neither are plain `.css`. Full file tree and drift notes: [references/realness-instance.md](references/realness-instance.md).

## Spacing: one unit, two axes

**Method**: pick one spacing unit. Derive every margin, gap, and padding from it as a multiple. Not arbitrary pixels, not `rem` guesses. When something feels off, check whether it's a multiple.

The unit governs two axes differently, because they mean different things physically:

**Vertical harmony is non-negotiable: vertical measurements are whole multiples.** Margin-top, margin-bottom, padding-top, padding-bottom, line-height - anything that decides where the _next_ line lands - must be `unit x 1`, `x 2`, `x 4`, never `x 0.35` or `x 0.5`. A fractional vertical multiple isn't a smaller version of the same rhythm - it's off the grid entirely, and it shifts every block beneath it out of phase with the page's baseline. That drift compounds down the page and breaks alignment between columns that don't happen to share the same fractional offset. Worked example of what this catches: [references/examples/vertical-rhythm-cleanup.md](references/examples/vertical-rhythm-cleanup.md).

From scott-fryxell/brayness, MIT.

More from scott-fryxell/brayness

Compare and alternatives

    Featured in

    Signals

    Stars
    125
    Forks
    15
    Open issues
    0
    Last commit (this folder)
    Sep 28, 2026 (29 days ago)

    From GitHub (fetched Oct 24, 2026).

    Details

    License
    MIT
    First seen
    Oct 24, 2026
    Last checked
    Oct 24, 2026

    More from scott-fryxell

    Cite this page

    useless skill. Agent Mart. https://agentmart.dev/skills/scott-fryxell-useless (last updated 2026-10-24)