Skip to content

feat: CSS library (for use in userstyles)#28

Open
ninetailedtori wants to merge 61 commits intocatppuccin:mainfrom
ninetailedtori:css
Open

feat: CSS library (for use in userstyles)#28
ninetailedtori wants to merge 61 commits intocatppuccin:mainfrom
ninetailedtori:css

Conversation

@ninetailedtori
Copy link
Copy Markdown
Contributor

@ninetailedtori ninetailedtori commented Feb 18, 2026

This PR adds CSS themes, that can be included in a userstyle easily, as such, example taken from my edit of Stylus Catppuccin, as a proof of successful usage, with a possible URL we can host on:

@import "https://userstyles.catppuccin.com/lib/lib.less";

@-moz-document url-prefix("moz-extension://"),
  url-prefix("chrome-extension://") {
    @import
    url("https://codemirror.catppuccin.com/@{lightFlavor}/codemirror-@{lightFlavor}-@{accentColor}.css")
    (prefers-color-scheme: light);
  @import
    url("https://codemirror.catppuccin.com/@{darkFlavor}/codemirror-@{darkFlavor}-@{accentColor}.css")
    (prefers-color-scheme: dark);
    
  :root {
    &[data-ui-theme="light"] {
      #catppuccin(@lightFlavor);
    }
    &[data-ui-theme="dark"] {
      #catppuccin(@darkFlavor);
    }
  }

  #catppuccin(@flavor) {
    #lib.palette();
    #lib.defaults();

    /* rest of theme here! */
  }
}

Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
@bajkani787

This comment was marked as spam.

@uncenter
Copy link
Copy Markdown
Member

uncenter commented Mar 4, 2026

I mentioned my feedback through Discord, but to summarize:

  1. We should avoid bringing in the new dependency of Tera here, and use scripting within the existing TypeScript setup. 2. We don't need to generate accent variants, or theme variants even - for the userstyles usecase, all we need is a single file using CSS variables.
  2. If we can use a (TypeScript) build script that can import the theme from the existing files (extract
    const theme = EditorView.theme(
    {
    "&": {
    color: colors.text.hex,
    backgroundColor: colors.base.hex,
    },
    ".cm-content": {
    caretColor: colors.rosewater.hex,
    },
    ".cm-cursor, .cm-dropCursor": {
    borderLeftColor: colors.rosewater.hex,
    },
    "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
    {
    backgroundColor: `${colors.overlay2.hex}40`
    },
    ".cm-panels": {
    backgroundColor: colors.mantle.hex,
    color: colors.text.hex,
    },
    ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
    ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" },
    ".cm-searchMatch": {
    backgroundColor: `${colors.blue.hex}59`,
    outline: `1px solid ${colors.blue.hex}`,
    },
    ".cm-searchMatch.cm-searchMatch-selected": {
    backgroundColor: `${colors.blue.hex}2f`,
    },
    ".cm-activeLine": { backgroundColor: colors.surface0.hex },
    ".cm-selectionMatch": {
    backgroundColor: `${colors.surface2.hex}4d`,
    },
    "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
    backgroundColor: `${colors.surface2.hex}47`,
    color: colors.text.hex,
    },
    ".cm-gutters": {
    backgroundColor: colors.base.hex,
    color: colors.subtext0.hex,
    border: "none",
    },
    ".cm-activeLineGutter": {
    backgroundColor: colors.surface0.hex,
    },
    ".cm-foldPlaceholder": {
    backgroundColor: "transparent",
    border: "none",
    color: colors.overlay0.hex,
    },
    ".cm-placeholder": {
    color: colors.overlay1.hex,
    },
    ".cm-tooltip": {
    border: "none",
    backgroundColor: colors.surface0.hex,
    },
    ".cm-tooltip .cm-tooltip-arrow:before": {
    borderTopColor: "transparent",
    borderBottomColor: "transparent",
    },
    ".cm-tooltip .cm-tooltip-arrow:after": {
    borderTopColor: colors.surface0.hex,
    borderBottomColor: colors.surface0.hex,
    },
    ".cm-tooltip-autocomplete": {
    "& > ul > li[aria-selected]": {
    backgroundColor: colors.surface1.hex,
    color: colors.text.hex,
    },
    },
    },
    { dark: isDark }
    );
    ) and use that, it would make maintaining this burdenless since it wouldn't be independently maintained.

I also wanted to note that the CSS here for the tokens is for CodeMirror v5 or earlier iirc. Nowadays in v6, the tokens are more obfuscated and we need to do something more like this:

g {
      color: @blue; // function/identifier
    }
    .ͼb {
      color: @mauve; // keywords
    }
    .ͼe {
      color: @green; // strings
    }
    .ͼf {
      color: @pink; // unknown? template strings?
    }
    .ͼl {
      color: @lavender; // object property
    }
    .ͼj {
      color: @teal; // class instantiation identifier
    }
    .ͼm {
      color: @overlay2; // comments
    }
    .ͼd {
      color: @peach; // numbers
    }
    .ͼc {
      color: @peach; // booleans
    }

I was trying to figure out if we could get these .ͼm classes from the @lezer/highlight tags stuff that you can see in https://github.com/catppuccin/codemirror/blob/main/src/catppuccin.ts, but I couldn't figure it out. It would be great if the token part was also synchronized/dependent on the existing configuration in this repository.

Signed-off-by: Toria <ninetailedtori@uwu.gal>
…y some js, clearly.

Signed-off-by: Toria <ninetailedtori@uwu.gal>
…is helps to know.

Signed-off-by: Toria <ninetailedtori@uwu.gal>
@uncenter
Copy link
Copy Markdown
Member

uncenter commented Mar 8, 2026

It's not clear to me why this is a Less file now. It should just be something like https://github.com/catppuccin/prismjs/blob/main/themes/variables.css, using CSS variables.

@uncenter
Copy link
Copy Markdown
Member

uncenter commented Mar 8, 2026

I also don't think userstyles-specific documentation should be in an unrelated port. If we were to use this in userstyles, we would document usage in the appropriate place - https://userstyles.catppuccin.com/contributing/guides/syntax-highlighting/.

Secondly, a key part of this is that it should address this earlier point:

2. import the theme from the existing files (extract

const theme = EditorView.theme(
{
"&": {
color: colors.text.hex,
backgroundColor: colors.base.hex,
},
".cm-content": {
caretColor: colors.rosewater.hex,
},
".cm-cursor, .cm-dropCursor": {
borderLeftColor: colors.rosewater.hex,
},
"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
{
backgroundColor: `${colors.overlay2.hex}40`
},
".cm-panels": {
backgroundColor: colors.mantle.hex,
color: colors.text.hex,
},
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" },
".cm-searchMatch": {
backgroundColor: `${colors.blue.hex}59`,
outline: `1px solid ${colors.blue.hex}`,
},
".cm-searchMatch.cm-searchMatch-selected": {
backgroundColor: `${colors.blue.hex}2f`,
},
".cm-activeLine": { backgroundColor: colors.surface0.hex },
".cm-selectionMatch": {
backgroundColor: `${colors.surface2.hex}4d`,
},
"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
backgroundColor: `${colors.surface2.hex}47`,
color: colors.text.hex,
},
".cm-gutters": {
backgroundColor: colors.base.hex,
color: colors.subtext0.hex,
border: "none",
},
".cm-activeLineGutter": {
backgroundColor: colors.surface0.hex,
},
".cm-foldPlaceholder": {
backgroundColor: "transparent",
border: "none",
color: colors.overlay0.hex,
},
".cm-placeholder": {
color: colors.overlay1.hex,
},
".cm-tooltip": {
border: "none",
backgroundColor: colors.surface0.hex,
},
".cm-tooltip .cm-tooltip-arrow:before": {
borderTopColor: "transparent",
borderBottomColor: "transparent",
},
".cm-tooltip .cm-tooltip-arrow:after": {
borderTopColor: colors.surface0.hex,
borderBottomColor: colors.surface0.hex,
},
".cm-tooltip-autocomplete": {
"& > ul > li[aria-selected]": {
backgroundColor: colors.surface1.hex,
color: colors.text.hex,
},
},
},
{ dark: isDark }
);

) and use that, it would make maintaining this burdenless since it wouldn't be independently maintained.

the token part was also synchronized/dependent on the existing configuration in this repository.

Signed-off-by: Toria <ninetailedtori@uwu.gal>
@ninetailedtori
Copy link
Copy Markdown
Contributor Author

ninetailedtori commented Mar 8, 2026

It's not clear to me why this is a Less file now. It should just be something like https://github.com/catppuccin/prismjs/blob/main/themes/variables.css, using CSS variables.

We're using accent and darken. Accent I think is covered by --ctp-accent, but I'm not sure how to implement darken with JUST css variables, we don't have access to that in raw css. We do have rgba for the values but then the css variables are given in hex, and wouldn't make sense to it. That would bring back the need for whiskers.

@ninetailedtori
Copy link
Copy Markdown
Contributor Author

It's not clear to me why this is a Less file now. It should just be something like https://github.com/catppuccin/prismjs/blob/main/themes/variables.css, using CSS variables.

We're using accent and darken. Accent I think is covered by --ctp-accent, but I'm not sure how to implement darken with JUST css variables, we don't have access to that in raw css. We do have rgba for the values but then the css variables are given in hex, and wouldn't make sense to it. That would bring back the need for whiskers.

Wait, no, did we discuss this and we had a way to do it? One sec, lemme see if I can fix that.

42willow and others added 6 commits March 8, 2026 23:47
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
@ninetailedtori
Copy link
Copy Markdown
Contributor Author

ninetailedtori commented Mar 8, 2026

I also don't think userstyles-specific documentation should be in an unrelated port. If we were to use this in userstyles, we would document usage in the appropriate place - https://userstyles.catppuccin.com/contributing/guides/syntax-highlighting/.

Secondly, a key part of this is that it should address this earlier point:

  1. import the theme from the existing files (extract
    const theme = EditorView.theme(
    {
    "&": {
    color: colors.text.hex,
    backgroundColor: colors.base.hex,
    },
    ".cm-content": {
    caretColor: colors.rosewater.hex,
    },
    ".cm-cursor, .cm-dropCursor": {
    borderLeftColor: colors.rosewater.hex,
    },
    "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
    {
    backgroundColor: `${colors.overlay2.hex}40`
    },
    ".cm-panels": {
    backgroundColor: colors.mantle.hex,
    color: colors.text.hex,
    },
    ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
    ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" },
    ".cm-searchMatch": {
    backgroundColor: `${colors.blue.hex}59`,
    outline: `1px solid ${colors.blue.hex}`,
    },
    ".cm-searchMatch.cm-searchMatch-selected": {
    backgroundColor: `${colors.blue.hex}2f`,
    },
    ".cm-activeLine": { backgroundColor: colors.surface0.hex },
    ".cm-selectionMatch": {
    backgroundColor: `${colors.surface2.hex}4d`,
    },
    "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
    backgroundColor: `${colors.surface2.hex}47`,
    color: colors.text.hex,
    },
    ".cm-gutters": {
    backgroundColor: colors.base.hex,
    color: colors.subtext0.hex,
    border: "none",
    },
    ".cm-activeLineGutter": {
    backgroundColor: colors.surface0.hex,
    },
    ".cm-foldPlaceholder": {
    backgroundColor: "transparent",
    border: "none",
    color: colors.overlay0.hex,
    },
    ".cm-placeholder": {
    color: colors.overlay1.hex,
    },
    ".cm-tooltip": {
    border: "none",
    backgroundColor: colors.surface0.hex,
    },
    ".cm-tooltip .cm-tooltip-arrow:before": {
    borderTopColor: "transparent",
    borderBottomColor: "transparent",
    },
    ".cm-tooltip .cm-tooltip-arrow:after": {
    borderTopColor: colors.surface0.hex,
    borderBottomColor: colors.surface0.hex,
    },
    ".cm-tooltip-autocomplete": {
    "& > ul > li[aria-selected]": {
    backgroundColor: colors.surface1.hex,
    color: colors.text.hex,
    },
    },
    },
    { dark: isDark }
    );

) and use that, it would make maintaining this burdenless since it wouldn't be independently maintained.

the token part was also synchronized/dependent on the existing configuration in this repository.

We can, but it's a bit painful. The method I used to output the eventual tokens, the epsilon ones, can be seen in one sec, and was also in an older commit, but my code had to render it to access the end stylesheet. It's complex and doesn't seem to include everything either. I have to patch that, until it's suitable to function for the final code.

Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
@ninetailedtori
Copy link
Copy Markdown
Contributor Author

ninetailedtori commented Mar 21, 2026

I'm not sure I'm gonna be able to find a method that accurately calculates this, without rendering it in the DOM, and even then, the way the lib works is that it ONLY renders what it has to see. Unfortunately not knowing exactly what triggers every single class they use, I also won't have an accurate way of getting this. I'm going to read up on the token-side of what is understood as what, and see if I can make a test render that works fully to convert what we have in our theme at the least.

Edit: I'm crazy, I'm mad, I absolutely cannot be stopped! I'VE DONE IT!!! And the cost is... cheap, since we ALREADY host the website and don't actively have to render ANYTHING, we're just scraping the style sheet from our test site :]

You may applaud now 😤

Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
Use the colors from our palette, silly!

P.S. Also, styling broke, oops.

Signed-off-by: Toria <ninetailedtori@uwu.gal>
it's more of a style thing given optimisations in the current engines making them pretty equal but...this is neater innit

Signed-off-by: Toria <ninetailedtori@uwu.gal>
ninetailedtori and others added 2 commits April 9, 2026 14:58
Co-authored-by: uncenter <uncenter@uncenter.dev>
Yes, it breaks our css and thus will be ENTIRELY IGNORED but we'll change it in another PR.
C.F. catppuccin#38

Signed-off-by: Toria <ninetailedtori@uwu.gal>
@uncenter
Copy link
Copy Markdown
Member

uncenter commented Apr 9, 2026

On a userstyles note, we ideally want a version that uses CSS variables to pull in.

Signed-off-by: Toria <ninetailedtori@uwu.gal>
Signed-off-by: Toria <ninetailedtori@uwu.gal>
ninetailedtori and others added 3 commits April 9, 2026 16:49
Co-authored-by: uncenter <uncenter@uncenter.dev>
Co-authored-by: uncenter <uncenter@uncenter.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants