Browse API 1 documentation
Styling and host appearance
Theme tokens, live palette updates, host scaling, and responsive extension CSS.
Mango gives extension documents the active app appearance without taking ownership of their layout. The host injects palette tokens as CSS custom properties, presents the page on a transparent WebView canvas, and makes CSS prefers-color-scheme follow the active Mango palette. The extension still owns every selector, reset, font, spacing rule, and component style.
There is no Mango stylesheet to import. Use the documented custom properties directly and provide browser-safe fallbacks so the page remains usable in ordinary browser development tools. Newer Mango 3.1 builds also inject optional button and inset-label field styles described below; existing extension controls are not changed automatically.
Theme tokens
Mango sets these properties on the document root before the entry document starts:
| Property | Intended use |
|---|---|
--grove-bg |
Main pane or page background. The host canvas is transparent, so transparent is also appropriate when the page should blend directly into Mango. |
--grove-surface |
Subtle raised or grouped surface, such as a result tile or secondary button. |
--grove-surface-strong |
More prominent raised surface or hover/selected state. |
--grove-text |
Primary text. |
--grove-text-secondary |
Supporting text, labels, and descriptions. |
--grove-text-tertiary |
Quiet metadata and placeholders. |
--grove-link |
Links and other link-like text. |
--grove-hairline |
Subtle separators and borders. |
--grove-hairline-strong |
More visible control borders and dividers. |
--grove-accent |
Primary accent, focus state, or action fill. |
--grove-accent-soft |
Translucent accent wash for selection, focus rings, and low-emphasis highlights. |
Values are complete CSS rgb(...) or rgba(...) colors. Treat their visual values as dynamic: a user can switch Mango themes while the extension remains running.
Use each token for its semantic role rather than assuming a particular hue or brightness. For example, --grove-accent is not guaranteed to be purple and --grove-bg is not guaranteed to be dark.
Start with a small reset
This is a useful baseline for a visible Sprig or settings page:
:root {
color-scheme: light dark;
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
}
* {
box-sizing: border-box;
}
html,
body {
min-width: 0;
min-height: 100%;
margin: 0;
}
body {
color: var(--grove-text, CanvasText);
background: transparent;
}
a {
color: var(--grove-link, LinkText);
}
Use background: var(--grove-bg, Canvas) instead when the document needs to paint an opaque page of its own. CanvasText, Canvas, GrayText, LinkText, Highlight, and ButtonFace are browser system colors that make useful fallbacks outside Mango.
Controls that fit Mango
Optional shared buttons
Mango 3.1 builds with the voice-recording UI update inject an opt-in grove-button class for native HTML buttons. Add grove-button-quiet for a transparent secondary action:
<button class="grove-button">Record</button>
<button class="grove-button grove-button-quiet">Discard</button>
The shared style uses live Grove palette tokens, rounded corners, hover and pressed backgrounds, a keyboard-focus outline, and disabled feedback. Hover applies only to enabled controls on hover-capable devices. The default minimum height is 28 CSS pixels (40 on coarse-pointer devices); the extension still owns layout and can override base sizing. Styling never applies to buttons without the class, and never to external web dashboards. It requires no new permission.
Keep local fallback styling for ordinary browsers and earlier Mango builds, which ignore these class names. No JavaScript capability detection is needed. Do not use the class as a security or host-version signal.
Optional inset-label fields
Newer Mango 3.1 builds inject an opt-in grove-field style. The extension supplies a semantic label and its own text; Mango does not add labels or restyle ordinary inputs:
<label class="grove-field">
<span class="grove-field-label">File name</span>
<input type="text" placeholder="voice-message" aria-label="File name (optional)">
</label>
The small muted label stays inside the border above the editable value. The field uses live palette tokens, hover feedback, a visible keyboard-focus outline around the wrapper, and disabled feedback. Use it for single-line text inputs, not checkboxes, file pickers, or other specialized input types. Only direct input children of label.grove-field receive these styles; existing Giphy, Immich, and other unmarked fields remain unchanged.
The minimum height is 38 CSS pixels (44 on coarse-pointer devices, with a 16px input font to avoid focus zoom). The extension still owns width, flex sizing, and spacing between controls. Hide the whole label wrapper with hidden when needed. Keep local fallback styling for older Mango builds and browser development, just as with grove-button. No new permission or JavaScript capability check is required.
Floating-label variant
Add grove-field-floating to the same wrapper for a button-height field. Its label sits inside while empty, then animates above the border on focus or when it has a value. The floated label is 9px (10px on touch devices); the field is at least 28px tall (40px on touch devices). Reduced-motion users get the same states without animation.
<label class="grove-field grove-field-floating">
<input type="text" placeholder="voice-message" aria-label="File name (optional)">
<span class="grove-field-label">File name</span>
</label>
Keep a placeholder attribute (" " is sufficient if no hint is needed): CSS uses :placeholder-shown to detect an empty field. The placeholder hint only appears while focused. Leave at least 16px above the field for the floating label, and do not clip it with an overflow-hidden wrapper. As with the inset variant, ship fallback CSS when supporting earlier hosts. Other fields remain unchanged.
Custom controls
Keep the control structure owned by the extension and compose it from host tokens:
input,
select,
textarea,
button {
min-width: 0;
border: 1px solid var(--grove-hairline-strong, color-mix(in srgb, CanvasText 24%, transparent));
border-radius: 0.55rem;
color: var(--grove-text, CanvasText);
background: var(--grove-surface, ButtonFace);
font: inherit;
}
input,
select,
textarea {
padding: 0.55rem 0.7rem;
}
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible {
outline: 2px solid var(--grove-accent, Highlight);
outline-offset: 2px;
}
button.primary {
border-color: transparent;
color: Canvas;
background: var(--grove-accent, Highlight);
}
.panel {
border: 1px solid var(--grove-hairline, color-mix(in srgb, CanvasText 18%, transparent));
border-radius: 0.8rem;
background: var(--grove-surface, color-mix(in srgb, CanvasText 7%, transparent));
}
.help {
color: var(--grove-text-secondary, GrayText);
}
Test primary-button text against every bundled Mango palette. A fixed light or dark foreground can lose contrast with a user-created theme; when the action does not need a filled treatment, an accent-colored label on --grove-surface is safer.
Live palette changes
Mango updates the root properties without reloading the document. CSS declarations that reference var(--grove-...) repaint automatically and preserve JavaScript state, scroll position, and form contents.
Do not read a token once with getComputedStyle(...), convert it to a hard-coded value, and expect that value to stay current. If JavaScript must draw into a canvas, re-read the computed token when matchMedia('(prefers-color-scheme: dark)') changes and whenever the page becomes visible again. Ordinary HTML and SVG should prefer CSS properties so no JavaScript synchronization is needed.
The resolved Mango palette also drives prefers-color-scheme, including when the palette’s light or dark appearance differs from the system setting. Use the media query for assets or layout choices that genuinely differ by appearance; use the Grove tokens for colors.
UI scale and CSS units
On macOS, Mango applies its current app UI scale to running extension views through WebKit page zoom. WebKit defines page zoom as scaling page content relative to the viewport, equivalent to applying CSS zoom to all page content. Mango reapplies it when the user changes the app’s typography size and applies it to newly created and retained extension WebViews.
The scale is host-owned and is not exposed as a CSS custom property or JavaScript value. Do not infer it from viewport dimensions and do not multiply font sizes, padding, icons, or controls by a second scale factor. A declaration such as font-size: 14px and one such as font-size: 1rem are both scaled by the host.
Relative units are still recommended for relationships inside the extension:
- use
remfor the extension’s typography and spacing scale; - use
emwhen a component should follow its own text size; - use
%,fr,minmax(...),min(...),max(...), andclamp(...)for flexible layout; - reserve CSS pixels for details such as one-pixel borders or a deliberate minimum.
Host zoom does not make a fixed layout responsive. A channel pane, network tab, composer surface, floating window, settings page, iPhone, and iPad can all provide different viewport sizes. Do not size the page from presentation.defaultSize; that value is only an initial host preference.
Current API 1 host zoom is a macOS behavior for running extension views. Extensions must remain usable at the browser’s ordinary CSS scale on iOS, iPadOS, settings pages, and browser-based development. This is why responsive CSS is part of the extension contract even when a Sprig primarily targets macOS.
Responsive surface recipe
Let the surface provide the outer size and let the document adapt:
.app {
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
width: 100%;
min-width: 0;
min-height: 100dvh;
padding: clamp(0.75rem, 2.5vw, 1.25rem);
gap: 0.75rem;
}
.results {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(9rem, 100%), 1fr));
min-width: 0;
overflow: auto;
}
@media (max-width: 28rem) {
.actions {
display: grid;
grid-template-columns: 1fr;
}
}
@media (pointer: coarse) {
button,
input,
select {
min-height: 44px;
}
}
Avoid viewport-wide fixed widths, fixed heights around scrolling content, and desktop-only hover interactions. Use min-width: 0 on grid and flex children that contain user or IRC text so long content cannot force the surface wider than Mango gives it.
Accessibility and motion
- Preserve visible keyboard focus with
:focus-visible. - Do not encode state only with color; add text, an icon, or another shape.
- Allow text to wrap and test with long localized labels.
- Respect
prefers-reduced-motionfor nonessential animation. - Use native semantic elements before recreating controls with generic elements.
- Test the extension in every bundled Mango palette, not only system Light and Dark.
Continue with Surfaces and scopes for the sizes and contexts in which a page can appear, and Storage and settings for the more restricted settings-page runtime.