How to make an AO3 work skin that doesn’t break

A work skin can fail without telling you. AO3 will refuse a stylesheet outright if it contains a property the archive does not allow — that failure is loud, and you can fix it. The failure worth planning for is the quiet one: the skin saves, the form says it saved, and some of your CSS is not there any more.

The short version: after you save a work skin, reopen it on AO3 and read the stored CSS back. Compare it with what you pasted. If rules are missing, remove every comment from your CSS and save again. Do not treat “it saved without an error” as proof that it saved intact.

This article is about the failure modes rather than the happy path, because the happy path is already well covered and it is not where people lose an evening. If you are not yet sure whether you want a work skin at all, start with work skin vs site skin vs public skin and come back.

Why “it looks right in the editor” proves nothing

Three different things render your fic, and they can disagree.

Where What it renders What it proves
Your editor or a local preview The CSS and HTML exactly as you wrote them That your code is valid CSS. Nothing about AO3
AO3’s skin preview The stylesheet as submitted, before storage That the form accepted it
The posted work What AO3 stored, applied to what AO3 stored of your chapter This is the only one that matters to a reader

Between the second row and the third sits AO3’s sanitizer, and it is not a linter that reports what it changed. It stores a cleaned copy and moves on. So the check that means anything is not “did it save” but “what is in there now”.

The four ways a work skin breaks

1. One disallowed property rejects the whole skin

AO3 accepts a fixed list of CSS properties. If your stylesheet uses one that is not on it, the archive rejects the entire skin — not the offending line — and the message does not name the property that caused it. This is the loud failure, and the fix is mechanical: find the property, replace it or drop it, save again.

Two things catch people here. The first is that an allowed property can still fail on its value: a web font, a calc() expression, or an image address the archive will not accept. The second is that shorthands behave differently from the longhand properties they expand to. The complete list, searchable, with the common replacements, is in every CSS property AO3 allows.

2. Comments are deleted on save — and rules near them can go too

AO3 removes every CSS comment when it stores a skin. That part is uncontroversial: comments are for you, they never reach a reader, and losing them costs nothing.

What is not uncontroversial is what happened to us on 7 August 2026, so here it is first-hand and without embellishment. We saved an iMessage work skin. It saved without an error. Read back, it was missing eleven consecutive rules: both bubble tails, the two .time rules and the image-time variant, the reaction rule, the status indicator, both attachment rules and both typing-row rules. Everything before them and everything after them survived. We sent 88 rules; 77 were stored. We reproduced it on a second save. Removing the comments from the export brought all eleven back, and they have never gone missing since.

We do not know the mechanism, and we are not going to invent one. The stylesheet was valid, every value satisfied AO3’s own rules, and the lost rules had nothing structurally in common beyond sitting between two comments. We know what fixed it. We do not know why it broke, and anyone who tells you they do — including us — should be asked for their evidence.

The practical advice needs no theory: send AO3 a stylesheet with no comments in it. Keep your commented copy somewhere you can edit it, and paste the stripped one. If you use our generator, this is already done — the work-skin export contains zero comments, precisely because of that afternoon.

3. AO3 injects paragraphs into your HTML

AO3’s editor adds paragraph and line-break elements around your markup, and how much it adds depends on how you paste. Skin authors have been working around this for years, with two defences that are better used together.

Defence one: leave no bare whitespace between elements. If every newline in your HTML sits inside an HTML comment, there is no loose whitespace for the editor to convert into a paragraph. This is why community skins are full of markup like <!--- at the end of a line: the comment swallows the line break and the source stays readable to a human.

Defence two: make injected paragraphs harmless. Reset paragraph spacing inside your own container, so that a paragraph AO3 adds takes up no room instead of pushing your layout apart:

The paragraph reset, as our exporter emits it
#workskin .chat p{margin:0;padding:0;}

Defence two matters more, because it does not depend on your paste being perfect — and pasting is the part you control least. Swap .chat for whatever class wraps your own layout. One caveat if you copy this pattern: the selector is more specific than a plain class selector, so any real paragraph of your own that needs its margins back has to be written at matching specificity or it will lose to this rule.

4. The parser may run again when you edit

This one is reported by other skin authors and we have not reproduced it, so treat it accordingly. The report is that AO3 re-runs its HTML parser whenever you add a chapter, edit an author’s note, or change HTML tags — meaning markup that posted correctly can be rewritten later by an edit that has nothing to do with it. One author documented a specific case in which a change to the parser stopped anchor tags from being siblings of div elements, and found that wrapping the structure in a figure element restored the old behaviour.

We are including it because if it is right, it changes what “working” means: your markup needs to survive being parsed repeatedly, not just once. We are attributing it because we have not seen it ourselves, and an unattributed claim that turns out to be wrong is expensive.

The check: save it, read it back, diff it

This is the part to actually do. It takes about five minutes and it is the only procedure that catches all four failure modes above, including the ones nobody has explained.

  1. Save the skin. On AO3: Preferences → Skins → Create Work Skin. Paste your CSS, give it a title you will recognise later, submit.
  2. Reopen it. Go back into that skin on AO3 and look at the CSS box. What you are seeing is not your file — it is AO3’s stored copy, cleaned and pretty-printed. That is exactly what makes it useful: it is a direct readout of what survived.
  3. Copy the stored CSS out into a file next to the one you pasted in.
  4. Compare the two. Any diff tool will do. You are not looking for formatting differences — the archive reformats everything, and your comments will be gone, which is expected. You are looking for whole rules that are absent.
  5. Count, as a fast sanity check. Count the { characters in each file: that is one per rule. If the stored count is lower than the sent count, something was dropped, and the diff will show you which selectors.
  6. If rules are missing, remove every comment from your CSS and repeat from step 1. If they come back, that was it. If they do not, you have found something new — write down exactly which rules and where they sat, because that detail is the whole finding.
The AO3 Create New Skin form with Type set to Work Skin and a stylesheet pasted into the CSS box
Step 1, what you sent. AO3’s Create New Skin form, with Type set to Work Skin and the stylesheet pasted in. Note what is in the box: one rule per line, no comments anywhere, and the paragraph reset sitting on the second line as #workskin .chat p{margin:0;padding:0;}.
The same AO3 work skin reopened after saving, showing the reformatted CSS the archive stored
Step 2, what AO3 stored. The same skin, reopened. The archive has reformatted everything — one declaration per line, indented and colour-coded — which is why you cannot diff on formatting. What you compare is the rules: #workskin .chat and #workskin .chat p are both here, with the same declarations that went in. This is the readout the whole procedure exists to get.

What a clean result looks like, from our own last run. Version 8 of our master work skin sends 1,194 rules and 3,511 declarations. Saved on real AO3 and read back, the stored copy contains 1,194 rules and 3,511 declarations — nothing dropped, at more than thirteen times the rule count of the skin that lost eleven rules in August.

Those numbers are printed by a script that reads them out of the exporter and the stored file. Nobody typed them, because a number a human has to remember to update is a number that goes stale.

Do this at every version of your skin, not once. A skin that stored cleanly in March tells you nothing about the one you edited last night, and the failure is silent by definition.

Check it with Creator’s Style switched off

Readers can turn work skins off, and some always do — on slow connections, with screen readers, or because they prefer AO3 plain. Downloads have no skin at all. So the last check is to read your chapter with Hide Creator’s Style on and ask whether it still makes sense.

The thing that usually breaks is attribution. A chat scene styled with CSS shows you who is speaking through bubble position and colour; strip the CSS and both sides look identical. The fix is to put the speaker in the markup, not only in the styling. Here is one message from our exporter, which carries a speaker label that is invisible with the skin on and read aloud with it off:

One message, as exported
<dl class="msg"><dt class="visually-hidden">Wren: </dt><dd class="bubble in has-tail"><span class="message-text">Did you save the skin, or just preview it?</span><span class="visually-hidden"> </span><span class="time">23:12</span></dd></dl>

Every message names its speaker exactly once. In a group chat, where the name is already visible, the hidden label is not added — emitting both would make a download say the name twice on every line.

Two constraints worth knowing before you build

A work can use one work skin. Not several. If your fic has a texting scene and a social-media scene, they have to live in the same stylesheet, which is what a combined or “master” skin is for. Selecting a second skin does not merge them.

Creating a skin does not attach it to anything. The skin lives in your preferences until you select it on the work itself, under Associations when you edit it. A surprising share of “my skin isn’t working” is this, and it costs nothing to check first.

One more habit worth borrowing: put a version marker in the skin, such as a rule naming a class like ao3skingen-v8. Months later, looking at a work that renders oddly, you will want to know which version of the stylesheet is actually installed on it, and the skin itself is the only thing that can tell you.

If a rule is missing, in order

  1. Remove every comment from the CSS and save again. This is the one with a proven history.
  2. Check the missing rule’s properties against the allowlist, and check its values too — an allowed property still fails on a rejected value.
  3. Confirm the skin is actually selected on the work, and that the chapter HTML went in through HTML mode rather than the rich-text editor.
  4. Re-read the stored CSS after every attempt. Changing something and assuming it worked is how the original problem stayed invisible.

The checklist

  • No comments in the CSS you paste into AO3.
  • Every property on the allowlist, and every value acceptable too.
  • A paragraph reset scoped to your own container.
  • No bare whitespace between elements in the chapter HTML.
  • Skin saved, reopened, read back, and diffed against what you sent.
  • Chapter read once with Creator’s Style off, and it still names its speakers.
  • One work skin per work, and it is selected on the work, not merely created.

If you would rather not hand-write any of this: the AO3 Skin Generator builds the scene, validates the CSS against AO3’s allowlist before it lets you copy anything, strips the comments, includes the paragraph reset and the hidden speaker labels, and gives you one skin covering every platform your fic uses. There is also a site skin maker if what you actually wanted was to restyle AO3 for yourself. It is unofficial and not connected to the OTW. Whatever you build with it, run the read-back check anyway — the archive is the only authority on what the archive stored.

Sources and further reading

Similar Posts