CSS migration guide
If you wrote custom CSS for your jam page, a few structural changes may affect you. This guide explains what moved and how to update your stylesheet.
We ran an automated script to migrate CSS files where we could. So you may not need to do anything, but we recommend checking your page just in case. There’s details about the automated migration at the bottom of this page. We took backups of every CSS file we edited in case you need us to help you view the diff.
This migration was intentionally conservative. We did not attempt to rewrite every possible custom CSS pattern. In particular, custom rules involving
body::before,body::after, unusual layout hacks, header selectors, or highly complex CSS may still need manual review. If your jam usedbodyfor full-page pseudo-element overlays, fixed decorative layers, or custom header positioning, please check your page against the migration guide.We recorded the diffs of all the changes in a private git repository for verification, if you’d like us to send you your diff please reply here. We also took archival screenshots of all auto-migrated jam pages before the theme editor migration in case you need to reference what the jam page used to look like.
Custom CSS is still injected the same way: appended verbatim to the end of the jam’s generated theme stylesheet, unscoped (global). We made markup changes that may affect how you did selectors though.
1. The page background moved from body to .jam_page_wrap
This is probably the biggest change. We moved how the background is applied. Going forward we want background styles to be on a wrapper element of the jam’s content instead of the body, to prevent messing with the itch.io UI.
The jam’s background color, background image, and text color used to be applied
to the body element. They are now applied to .jam_page_wrap.
Before:
body {
color: <text_color>;
background-color: <bg_color>;
background-image: url(...); /* if a background image was set */
}
After:
.jam_page_wrap {
color: <text_color>;
background-color: <bg_color>;
background-image: url(...);
}
The body element no longer carries the jam background or text color.
If you set a page background on body, move that rule to .jam_page_wrap:
/* old */
body {
background: #1a1a2e url(/my-bg.png) repeat;
}
/* new */
.jam_page_wrap {
background: #1a1a2e url(/my-bg.png) repeat;
}
You can still target body for things that are genuinely page-wide, but be
aware your rule now sits behind .jam_page_wrap, which paints its own
background on top. In most cases you want .jam_page_wrap.
2. The site header moved out of .jam_page_wrap
.jam_page_wrap used to wrap both the top header and the jam content. Now the
header is rendered as a sibling before .jam_page_wrap, and .jam_page_wrap
wraps only the jam page content.
Old structure (visitor):
body
.jam_page_wrap
<header>
.view_jam_page (the jam content)
New structure (visitor):
body
<header>
.jam_page_wrap
.view_jam_page (the jam content)
If you wrote descendant selectors that assumed the header was inside the
wrap (for example .jam_page_wrap .header ...), those no longer match. Target
the header directly instead.
3. New wrapper elements when you are editing your own jam
When you view a jam you can edit, the page now renders the live theme editor beside the content, which adds two wrapper elements:
body
<header>
.theme_editor_columns
<theme editor sidebar>
.theme_editor_primary_column
.jam_page_wrap
.view_jam_page
Regular visitors do not get these wrappers; they see the simpler structure
from section 2. This matters because .jam_page_wrap is no longer guaranteed to
be a direct child of body. Avoid relying on body > .jam_page_wrap; use a
plain .jam_page_wrap selector.
4. Many built-in styles are now CSS variables on .jam_page_wrap
The generated theme stylesheet used to hardcode your colors into a long list of
specific selectors. Most of those now set CSS custom properties on
.jam_page_wrap, and the site’s static stylesheet reads them with
var(--name, fallback). (Because .jam_page_wrap is an ancestor of
.view_jam_page, the variables still cascade down to the page content.)
New variables set on .jam_page_wrap:
--itchio_link_color
--itchio_border_color
--itchio_border_radius
--itchio_button_color
--itchio_button_fg_color
--itchio_button_shadow_color
--itchio_button_border_color
--itchio_button_hover_color
--itchio_button_active_color
These selectors were removed from the generated theme CSS because the values are now delivered through those variables:
.view_jam_page a { color: ... }— links read--itchio_link_color.view_jam_page .button { ... }and its:hover/:activerules — buttons read the--itchio_button_*variables (the old!importanton hover/active is gone).view_jam_page .jam_submitter_widget, .jam_header_widget, .stat_box { border-color }.view_jam_page .formatted hr { background-color }.view_jam_page .grid_outer .header_row { border-color }.view_jam_page .jam_filter_picker { border-color }.view_jam_page .header_tabs .header_tab.active / :hover { border-color }— now uses--itchio_button_color.view_jam_page .stats_container a:hover { color }— now uses the link color
What this means for you:
-
Overriding those colors: your old overrides on the specific selectors still work, but the supported (and simpler) way now is to set the variable on
.jam_page_wrap:.jam_page_wrap { --itchio_link_color: #ff66cc; --itchio_button_color: #00aaff; --itchio_border_radius: 8px; } -
!importanthacks for buttons: if you used!importantto beat the old.button:hover { ... !important }rule, you can probably drop it now, since that!importantrule no longer exists.
Quick checklist
- Move any
body { background: ... }rule to.jam_page_wrap. - Drop assumptions that the header lives inside
.jam_page_wrap; it is now a sibling before it. - Don’t rely on
body > .jam_page_wrap; the wrap can be nested deeper when the owner is editing. - Prefer setting
--itchio_link_color,--itchio_button_color, etc. on.jam_page_wrapover overriding the old per-element selectors. - Remove
!importantyou only needed to beat the old button hover/active rules.
Automatic Custom CSS Migration
Here’s the list of automatic migrations we made to existing jam CSS:
body → .jam_page_wrap migration
The main markup change is that jam page backgrounds and inherited theme styling now live on .jam_page_wrap instead of body. To account for that, we updated many existing custom CSS rules that were using body for jam-specific presentation.
In particular, when we found body rules containing page theme styles, we moved those declarations to .jam_page_wrap, including:
background
background-color
background-image
background-size
background-position
background-repeat
background-attachment
color
font
font-family
font-size
font-weight
line-height
letter-spacing
text-align
text-transform
For animated backgrounds, we also moved related declarations such as animation and image-rendering when they appeared alongside background styling. This keeps common animated background effects working after the background target changes.
When a body rule contained both moved and unmoved declarations, we split it: jam-page styling was moved to .jam_page_wrap, and unrelated body declarations were left alone. Empty body {} rules created by the migration were removed.
We also handled simple comma-separated selector cases. For example, rules like this:
body,
.some_other_selector {
font-family: sans-serif;
}
were rewritten so the .jam_page_wrap receives the migrated theme declarations without changing the unrelated selector more than necessary.
Removal of #wrapper selectors
Separately from the body → .jam_page_wrap migration above, the updated jam page now includes a page wrapper element with the id wrapper. Jam pages never had an element with that id before, so any custom CSS that targeted #wrapper previously matched nothing and had no effect.
Now that #wrapper exists, those previously-inert rules would suddenly start applying and could unexpectedly shift or break your page layout. We assume these creators copied their CSS over from game pages, which do have a #wrapper element, and most likely left the #wrapper rules in place because they had no effect on the jam page previously.
To keep these pages rendering exactly as they always have, we removed the #wrapper usage so it stays inert:
- Rules that styled
#wrapperdirectly, or only its descendants (e.g.#wrapper .inner_column { ... }), were removed, since they never applied. - Where
#wrapperwas only one selector in a comma-separated group (e.g.#wrapper, .hidden { display: none }), we dropped just the#wrapperpart and left the other selectors untouched.
Because these rules were already doing nothing before the wrapper element was introduced, this cleanup was not intended to change how your page looks.