Skip to content

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

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

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

Conversation

@ninetailedtori
Copy link

@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
Copy link

hello dear

dear please i need flask software

@uncenter
Copy link
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
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
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
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
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
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>
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.

5 participants