Style a slop
Give the app one clear visual identity: paper for an invoice or note, an instrument for a timer or mixer, or a custom skin when the silhouette helps its job. Keep the primary action obvious at the initial window size. Build depth from a backing surface and purposeful controls rather than repeated generic cards.
Declare the theme palette
Section titled “Declare the theme palette”slop.ts’s theme declares the colors a person may change. Every value is a lowercase #rrggbb color, or #rrggbbaa when it is translucent (an opaque color leaves out ff):
theme: { surface: "#f7f4eb", panel: "#e3e9df", ink: "#182126", muted: "#687276", accent: "#db6648", accentSoft: "#db66482e", },The builder stores these defaults in the template and refuses anything that is not a palette color. The runtime applies them, with the person’s changes, before mounting the app. Each key becomes --slop-KEY, preserving case. Declare only colors worth changing: the theme panel lists every one, in the order you wrote them.
Fonts, sizes and colors derived from the palette belong in your CSS. A derived color that mixes a theme color follows the person’s change automatically. Do not maintain a second defaults stylesheet.
Write ordinary CSS
Section titled “Write ordinary CSS”Import styles.css explicitly in slop.ts. Vite emits a separate, minified ui.css, including component styles and dependencies reached through CSS @import and url(). Styles ship as ordinary CSS.
:root { --font: "Avenir Next", Avenir, sans-serif; --rule: color-mix(in srgb, var(--slop-ink) 15%, transparent);}body { color: var(--slop-ink); background: var(--slop-surface); font-family: var(--font); }.my-slop-paper { padding: clamp(1rem, 5vw, 2.5rem); }.my-slop-footer { border-top: 1px dashed var(--rule); background: var(--slop-panel); & > span { font-size: 12px; color: var(--slop-muted); } & button { border: 2px solid var(--slop-ink); background: var(--slop-accent); }}@media (max-width: 360px) { .my-slop-paper { padding: 1rem; } }Use readable app-prefixed classes in markup. Group descendants and interaction states with their base component. Nesting is supported and lowered for the host’s Safari target. Use flex/grid reflow, container queries, visible focus, and reduced-motion styles. Keep controls clear of transparent edges.
Bits UI menus can render in portals. Give the portal content its own class and target attributes directly, such as .my-slop-menu [data-highlighted]; a selector below the editor root cannot reach a portal outside that root. Svelte <style> blocks remain available for component-local rules, but plain CSS is the starter convention.
Text selection
Section titled “Text selection”The host disables ordinary text selection in every slop’s editor, including slop dev, so dragging across labels and controls feels like using an app. Inputs, textareas (including readonly fields), and contenteditable regions keep normal text selection and editing. Captures and PDF text are unaffected.
Enable selection for content people may want to copy, such as notes, addresses, or code. Add an ordinary rule to styles.css; the host default has zero specificity and needs no !important override:
/* Allow selection in a content region, including its descendants. */.my-slop-content { -webkit-user-select: text; user-select: text;}
/* Or allow selection throughout the slop, including portals. */body { -webkit-user-select: text; user-select: text;}Choose the region rule or the whole-slop rule to suit your app. Put the region’s class on portal content too when it should be copyable. This is an authoring CSS choice, not a theme setting.
Let people change the colors
Section titled “Let people change the colors”In the Mac app, the palette button in a document’s toolbar, or Window ▸ Theme, opens the theme panel beside the window. It shows a color picker and hex field for every declared color. Changes restyle the slop as you make them and save with the document like any edit. Reset to Original returns every color to the template’s. Edit ▸ Undo restores the previous colors; a color-picker drag is one undo step.
Export Theme… saves the document’s full palette as a theme file. Import Theme… replaces the palette with one exported from a document made with the same template; colors the file leaves out return to the template’s. A file made for another template, or naming a color this template doesn’t declare, is refused whole.
The CLI does the same:
bunx hitslop theme get My.slopbunx hitslop theme set My.slop --values '{"accent":"#7050ad","surface":"#f5efff"}'bunx hitslop theme reset My.slop --token accentbunx hitslop theme reset My.slopbunx hitslop theme export My.slop --output Purple.jsonbunx hitslop theme import Other.slop Purple.jsonA theme file names its template and every color:
{ "template" : "my-slop", "values" : { "accent" : "#7050ad", "surface" : "#f5efff" }}Themes live inside the .slop file:
| Part | Purpose |
|---|---|
| Theme defaults | Immutable, from the author’s slop.ts when the template is built. |
| Theme overrides | The colors this writable document changed, saved with its contents. |
Only declared colors can change, and a document stores only the ones that differ from the template’s. An open window applies accepted CLI changes at once; unknown colors and invalid values are refused. Fonts and layout stay authored. Registered masters and generated templates remain unchanged.
Design the window and its captures
Section titled “Design the window and its captures”Every presentation mode receives host page resets, window sizing, and initial size variables. The automatic app root fills the window; prefer height: 100%, grid, or flex inside it; override the page sizing deliberately only when your layout needs it. Keep surface styling in your app. Transparent and PNG-skinned modes also override ordinary html/body backgrounds with transparency, so paint the visible surface inside the wrapper. Use the native toolbar handle for dragging. See manifest and windows and PNG window skins.
Dedicated icon and export views render saved document data and theme in a fresh read-only page. Export layouts should use normal flow, without nested scrolling or fixed viewport heights. Without an export view, a fresh rendering of your view is the fallback; mark editor-only elements data-slop-export="hide". See icons and exports for capture sizing and behavior.
See how .slop files work for how a theme travels with a document.