Skip to content

feat(carbon): init - #2286

Open
DurianBurger561 wants to merge 34 commits into
catppuccin:mainfrom
DurianBurger561:feat/carbon
Open

feat(carbon): init#2286
DurianBurger561 wants to merge 34 commits into
catppuccin:mainfrom
DurianBurger561:feat/carbon

Conversation

@DurianBurger561

@DurianBurger561 DurianBurger561 commented Jul 7, 2026

Copy link
Copy Markdown

Website: Carbon

URL: https://carbon.now.sh

Description:

Carbon is a web app for creating and sharing images of source code. This userstyle applies Catppuccin colors to Carbon's UI, editor/export preview, syntax highlighting, controls, menus, and export-related surfaces.

🗒 Checklist 🗒


  • I have read and followed Catppuccin's userstyle submission guidelines.
  • I have made a new directory underneath /styles/<name-of-website>.
    • I have ensured that the new directory is in lower-kebab-case.
    • I have named the userstyle catppuccin.user.less within the new directory.
    • I have followed the template and kept the preprocessor as LESS.
  • I have updated the userstyles.yml file with information about the new userstyle.

💬 Comments 💬

Tested all four flavours (Latte, Frappé, Macchiato, Mocha), accent switching, and Carbon PNG/SVG export with Stylus enabled.

@uncenter uncenter changed the title feat(carbon): add userstyle feat(carbon): init Jul 7, 2026
@DurianBurger561

Copy link
Copy Markdown
Author

Hi, sorry to bother you. I just wanted to ask if there is any estimated timeline for the review. Thank you!

@uncenter

uncenter commented Jul 9, 2026

Copy link
Copy Markdown
Member

Hi, we are unfortunately unable to review this pull request until it meets our contributing guidelines, which require you to use our pull request template https://github.com/catppuccin/userstyles/blob/main/.github/PULL_REQUEST_TEMPLATE/userstyle-creation.md.

@DurianBurger561

Copy link
Copy Markdown
Author

Thanks for letting me know! I’ve updated the pull request description to use the required template.

Comment thread styles/carbon/catppuccin.user.less Outdated
Comment thread styles/carbon/catppuccin.user.less Outdated
Comment thread styles/carbon/catppuccin.user.less Outdated
Comment thread styles/carbon/catppuccin.user.less
Comment thread styles/carbon/catppuccin.user.less Outdated
- Revert @Version to the template default (2000.01.01); CI bumps it.
- Drop the unused #lib.css-variables() call since nothing here reads
  --ctp-* custom properties.
- Replace the `* { scrollbar-color: ... }` wildcard with a plain
  root-level declaration.
- Remove all !important flags, instead tripling the :root selector
  at the mixin call site so every rule inherits enough specificity
  to win without it.
@DurianBurger561

Copy link
Copy Markdown
Author

Thanks for the feedback, I think I fixed everything ([9e539ccd](DurianBurger561@9e539ccd)): version back to 2000.01.01 (my bad, forgot CI bumps that), removed the unneeded css-variables() call, moved the scrollbar rule off the * wildcard onto the root, and got rid of all the !important's by tripling the :root selector where the mixin gets called so specificity is high enough without them (tested it compiles fine). Left the CodeMirror part as manual overrides since I didn't see a shared mixin for it in lib.less yet, can switch it over whenever that's ready — let me know if anything still looks off!

@DurianBurger561

This comment was marked as spam.

@DurianBurger561

This comment was marked as spam.

@scarcekoi

Copy link
Copy Markdown
Member

The theme looks unfinished and broken:
image

And changing the editor colours should probably be done via Carbon's palette feature in a port and this userstyle be for the rest of the site so that changing to other themes is still possible.

sixuan911 and others added 2 commits July 16, 2026 15:42
Carbon has its own palette feature for the code editor's colours
(Blackboard, Dracula, etc.). Overriding CodeMirror/token colours here
fights that and leaves users unable to pick another editor theme, per
scarcekoi's review. This userstyle now only themes the surrounding
site chrome.
@uncenter uncenter added the 0.kind: init A new userstyle label Jul 20, 2026
@DurianBurger561

Copy link
Copy Markdown
Author

Made all the requested changes Should be good to go now; would appreciate a re-review when you get a chance

@scarcekoi

scarcekoi commented Aug 24, 2026

Copy link
Copy Markdown
Member

The theme still has misthemed and unthemed elements:
image

E.g. the dropdowns look broken because the main background is one colour while the text background is another, as well as the header at the top being unthemed.

Carbon's outer "editor" div (which wraps the toolbar and code card)
kept its own near-black background since our Editor/Preview/Container
selectors only set color/border-color, not background-color. That
left a visible black seam around the toolbar and language dropdown,
which is what scarcekoi's screenshot was showing. Verified live on
carbon.now.sh by patching the site's actual CSS in devtools before
and after.

Also updated the lib import to the new versioned path
(lib/std/v1.less) to match the rest of the repo after the standard
library migration.
The `html, body, #__next, main` rule sits inside `:root:root:root`,
which LESS expands as a descendant combinator (`:root:root:root
html`). <html> has no ancestor element, so that compiled selector can
never match — the background/color on <html> itself was silently
dead code, letting Carbon's own near-black background show through at
the page edges.

Verified live on carbon.now.sh: before this fix, elementFromPoint(10,
10) resolved to <html> with its original rgb(18,18,18) background
even with the rest of the page themed. Fixed by using `html&` so LESS
compiles it to the compound selector `html:root:root:root` (same
element, not a descendant), confirmed the background now applies
correctly.
Carbon draws a 1px/2px white inset box-shadow around a dropdown
trigger (e.g. the language selector) while it's open, as its own
"active" indicator — unrelated to background-color. Left uncolored,
that white ring made the trigger look like a different, lighter shade
than the rest of the dropdown, which is what the report about
"light/dark mismatch" in the open dropdown was actually showing.
Recolored it to the configured accent instead of leaving Carbon's
white. Verified live via devtools: before, computed box-shadow on the
expanded trigger was `rgb(255,255,255) ... inset`; after, it's the
accent color.
Carbon's dropdown triggers (theme selector, searchable language
picker) are a <span class="...dropdown-display"> wrapping an
<input>. Both matched two of our rules with EQUAL specificity —
"Shared surfaces" via [class*="dropdown"] (mantle) and "Controls" via
the bare `input`/`button` element selector (surface0) — so the tie
broke on source order per element: the [role="button"] wrapper span
also matches an attribute selector in Controls and won there, but the
plain <input> only matched Controls via the low-specificity bare tag
and lost to Shared surfaces instead. Result: the wrapper rendered
surface0 while the input inside it rendered mantle, a visible two-tone
seam within what should be one control.

Fixed by adding `:not(:disabled)` (a functional no-op) to the plain
button/input/select/textarea selectors in Controls, bumping them to
attribute-selector specificity so they reliably win. Verified live via
devtools: before, span bg was #313244 and the input inside it was
#181825; after, both are #313244.
The previous fix only addressed the resting-state background mismatch
between a dropdown trigger's wrapper span and its nested search
<input>. The hover/active rules had the same gap: they only listed
button/[role="button"]/[class*="button"], never input/select/textarea,
so on hover the wrapper (matching [role="button"]:hover) would shift
to surface1 while the input inside it stayed unaffected — reintroducing
the two-tone mismatch, just in the hover state instead of the resting
one. Added input/select/textarea to both the :hover and :active rule
groups so the whole control changes shade together.
Carbon's dropdown triggers (theme selector, language picker) contain
a small caret svg, so they also matched the "icon buttons" rule
(button/[role="button"]:has(svg)), which hovers to lavender — a
different color than the plain accent hover the trigger's nested
<input> gets from Controls. Excluded dropdown triggers from the
icon-button rule with :not([class*="Dropdown"]):not([class*="dropdown"]),
since that rule is meant for icon-only buttons (settings, copy), not
text+caret comboboxes.

Also pinned border-color: transparent on the trigger (matched via its
own "...dropdown-display" class) so hover/active border-color changes
never show as a visible border there — the accent box-shadow ring
added earlier is the only "active" indicator now, avoiding a doubled
ring/border look.
The border-color: transparent rule for dropdown triggers tied in
specificity with the button/input :hover and :active rules, which come
later in source order and therefore won on hover — so the trigger's
border flashed to the accent color on hover again despite the earlier
fix. Raised the rule's specificity (doubled [class] attribute selector
plus explicit :hover/:active variants) so it always beats those, in
every interaction state. Verified resting-state border stays
transparent; specificity now (0,7,0) vs the hover rules' (0,5,0).
Root cause of the recurring dropdown color mismatch: Carbon's trigger
is a wrapper span (...dropdown-display) with a search input
(...dropdown-display-text) layered inside, and both carried their own
background from the broad Controls/Shared rules. Because :hover only
applies to the element actually under the cursor, the wrapper and the
input could sit in different hover states and render as two shades —
which is why fixing one interaction state kept surfacing the seam in
another.

Fix it structurally instead of per-state: make the inner input's
background fully transparent so it always reveals the wrapper's color,
regardless of which element is hovered. The wrapper alone now owns the
visible surface. Kept the border-transparent pin on the pair.
Verified the -text selector matches only the input, not the wrapper.
The real "inner box" users saw was Carbon's own white inset box-shadow
ring on the dropdown trigger (1px when closed, 2px .is-open). The
earlier accent recolor only targeted the aria-expanded="true" open
state, so the closed-state white ring survived — and even recolored it
still looked like a mismatched inner rectangle. Its specificity
(.dropdown-display.is-open, 0,3,0) also beat the recolor in some
states.

Flatten the trigger into a single surface instead: box-shadow: none in
all states (specificity raised above Carbon's ring), border-color
transparent, and the inner input's background transparent so it always
reveals the wrapper. No inner ring, no two-tone, in resting/hover/
active/open. Verified live: computed box-shadow is now none.
Even after removing the inset ring, the open dropdown still looked
two-tone: the trigger bar matched the Controls rule (surface0, lighter)
while its own popup list and items are mantle (darker), so the trigger
read as a lighter "outer" bar over a darker list. Pinned the trigger
wrapper's background to mantle in all states so the whole dropdown —
trigger, container, list, and items — is one consistent surface. The
inner input stays transparent and reveals the wrapper, so no seam
appears in any state. Verified live: trigger and list items now both
render mantle.
The two-tone seam also affected the open list: each row is a
li[role="option"] wrapping a span...dropdown-list-item-text. On hover
the row went surface0 but the inner text span kept its own mantle
background, so hovered rows showed a lighter bar with a darker inner
strip. Generalized the transparent-text rule from just the trigger
input (dropdown-display-text) to every dropdown text child
([class*="dropdown"][class*="-text"]) — which matches the trigger
input and the list-row labels but never the wrappers/rows — so the
label always reveals its row's background in every state. Verified
live: the list-item text span computes to transparent.
The export popout had two background problems: the popout container
itself matched none of our surface selectors (only "popover", not
"popout") so it kept Carbon's default near-black, and its `export-row`
containers matched the broad [class*="export"] accent rule, painting
each File name / Size / Open / Download row with an accent tint and
accent text — a lighter/tinted inner block over the darker popout.

Added Popout/popout to the Shared surfaces group so the container is
mantle like other menus, and excluded row containers from the Export
accent rule with :not([class*="row"]) so the rows read as part of the
popout surface while the toolbar's export-container keeps its accent
highlight. Verified live: popout is mantle, export-row transparent
with normal text, and the toolbar Export button stays accented.
After theming the popout mantle, its filename input still took the
Controls surface0 and read as a lighter box floating on the darker
panel. Gave inputs inside the popout the mantle surface so the field
blends into the panel; the surface1 border it already gets from
Controls keeps it legible as an editable input. Verified live: the
filename field now computes to mantle and the popout reads as one
cohesive surface.
Extended the popout-blend fix from just the filename input to the
popout's buttons — the 1x/2x/4x size options, Open, and Download
PNG/SVG — which were still surface0 boxes on the mantle panel. They
now take the mantle surface and get a surface0 accent hover for
feedback, so the whole export popout reads as one cohesive surface
with no lighter control boxes. Verified live: all six popout buttons
compute to mantle.
The window title input (aria-label "Image title") at the top of the
code preview card took the Controls surface0 and showed as a lighter
box in the card's title bar, whose backdrop is the user's export
background, not one of our surfaces. Made that input transparent
(background and border) so it reveals the card behind it instead of
painting a box over it. Verified live: the title field now computes to
transparent and the title bar reads cleanly.
The card's title bar (window-controls) matched the Controls rule via
[class*="control"] and took surface0, showing as a lighter strip over
the card's own background — the card is the user's artwork, themed by
their chosen export background, not one of our surfaces. Made
window-controls transparent (merged with the existing Image-title
input rule) so the title bar reveals the card behind it, matching
stock Carbon where the title bar is the card background. Verified live:
window-controls computes to transparent.
The real cause of the lighter title bar: the Preview/export mantle rule
included [class*="Window"]/[class*="window"], and it came after (and at
equal specificity to) the window-controls transparent rule, so it won
and repainted the card's title bar mantle. Those selectors only ever
match the code card's own window-controls (title bar) and window-theme
(code container) — the user's artwork — so removed them from the rule.
Combined with the existing transparent window-controls rule, the title
bar now reveals the card background like stock Carbon. Verified live:
window-controls is transparent and the title bar matches the card.
Carbon hard-codes `background-color: #1f1f1f !important` on the hover
(and :focus) of several action buttons — Tweet, Export, the copy and
background-select buttons — so on hover they flipped to near-black.
That reads fine against the dark flavors but is a jarring, high-
contrast slab in the light flavors. Our regular hover rules lost to
Carbon's !important, so added !important to the hover/active
background-color of the control and icon-button rules — overriding an
upstream !important, the one sanctioned use. Verified live: the Export
button now hovers to surface1 (rgb(188,192,204) in Latte) instead of
rgb(31,31,31).
The previous commit added `background-color: @Surface1 !important` to
the control hover rule (to beat Carbon's black hover). That !important
also hit the code card's title field on hover, repainting it as a grey
box in the title bar. Made the window-controls / Image-title
transparent rule !important too so it wins over the hover rule and the
title bar stays part of the card in every state.
@DurianBurger561

Copy link
Copy Markdown
Author

hi, please review it, all the theme background issues fixed

@uncenter uncenter added the 5.needs: staff-review A review by a staff member is required/needed label Aug 25, 2026
@scarcekoi

Copy link
Copy Markdown
Member

Dropdown borders are unthemed and missing from two of the boxes as well as the github link in the top right.

imageimage

scarcekoi noted the dropdown/box outlines were unthemed and missing on
two boxes and the GitHub link. Root cause: Carbon draws button outlines
with `box-shadow: #fff 0 0 0 1px inset` (a white inset ring), which our
border-color theming never touched, and the dropdown rule had zeroed
its ring entirely (so the two dropdowns lost their outline). Recolored
the ring to a themed surface1 on the dropdown triggers and the icon
buttons, and added an explicit rule for the GitHub sign-in button
(its icon is a CSS background, not an <svg>, so it missed the
icon-button rule). Verified live: dropdowns, icon buttons, and the
GitHub button now show a subtle surface1 outline instead of white.
The themed ring added for the unthemed-border fix matched both the
dropdown wrapper (...dropdown-display) and its inner search input
(...dropdown-display-text, whose class also contains "dropdown-display"),
so both drew the inset ring and it read as a doubled inner/outer
border. Excluded the -text input from the ring rule (:not([class*="-text"]))
and pinned box-shadow:none on the dropdown text elements, so only the
outer wrapper carries the single themed outline. Verified live: wrapper
box-shadow is surface1, the inner input's is none.
Tweet and Export are split buttons (text half + dropdown-arrow half).
Carbon rings both halves the same colour, but the arrow half has an svg
so the icon-button rule recolored only it to surface1, leaving the text
half on Carbon's colour — each split button showed a two-tone, broken-
looking outline. Recolored both halves per container to one themed ring
(Tweet blue via share-menu-container, Export accent via
export-menu-container), !important to beat the icon-button rule.
Verified live: both halves of Tweet are blue, both halves of Export are
accent.
A full audit of the toolbar rings turned up one box still on Carbon's
white inset ring: the background-colour picker swatch
(bg-select-display), a button with no svg that missed the icon-button
rule. Recolored just its ring to surface1 (its background stays the
colour preview). Re-audited all ten toolbar boxes: no white rings and
none missing.
The bright border still visible on the dropdowns was the leading icon
cell (dropdown-icon — the palette/globe box), a separate element from
dropdown-display that kept Carbon's white inset ring, so each dropdown
showed a themed border on one half and a white one on the icon half.
Recolored dropdown-icon's ring to surface1. A full-page scan now finds
zero white box-shadow rings anywhere.
@DurianBurger561

Copy link
Copy Markdown
Author

Hi, I made a change. if there are still any problem, can you give me a specific details about it, thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

0.kind: init A new userstyle 5.needs: staff-review A review by a staff member is required/needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants