├── .changeset
├── README.md
└── config.json
├── .github
└── workflows
│ ├── deploy-docs.yml
│ └── release.yml
├── .gitignore
├── .npmrc
├── README.md
├── docs
├── .gitignore
├── README.md
├── astro.config.mjs
├── ec.config.mjs
├── package.json
├── public
│ ├── favicon.svg
│ └── og.png
├── src
│ ├── content
│ │ ├── config.ts
│ │ └── docs
│ │ │ ├── configuration.mdx
│ │ │ ├── getting-started.mdx
│ │ │ └── index.mdx
│ ├── env.d.ts
│ └── styles.css
└── tsconfig.json
├── package.json
├── packages
└── expressive-code-color-chips
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ ├── colorRegEx.ts
│ └── index.ts
│ └── tsconfig.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.3/schema.json",
3 | "changelog": ["@changesets/changelog-github", { "repo": "delucis/expressive-code-color-chips" }],
4 | "commit": false,
5 | "fixed": [],
6 | "linked": [],
7 | "access": "public",
8 | "baseBranch": "main",
9 | "updateInternalDependencies": "patch",
10 | "ignore": ["@expressive-code-color-chips/*"]
11 | }
12 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-docs.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | on:
4 | # Trigger the workflow every time you push to the `main` branch
5 | # Using a different branch name? Replace `main` with your branch’s name
6 | push:
7 | branches: [main]
8 | # Allows you to run this workflow manually from the Actions tab on GitHub.
9 | workflow_dispatch:
10 |
11 | # Allow this job to clone the repo and create a page deployment
12 | permissions:
13 | contents: read
14 | pages: write
15 | id-token: write
16 |
17 | jobs:
18 | build:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: pnpm/action-setup@v3
23 | - uses: actions/setup-node@v4
24 | with:
25 | node-version: 20
26 | cache: pnpm
27 | - run: pnpm i
28 | - run: pnpm build
29 | - run: pnpm build:docs
30 | - uses: actions/upload-pages-artifact@v3
31 | with:
32 | path: docs/dist/
33 |
34 | deploy:
35 | needs: build
36 | runs-on: ubuntu-latest
37 | environment:
38 | name: github-pages
39 | url: ${{ steps.deployment.outputs.page_url }}
40 | steps:
41 | - name: Deploy to GitHub Pages
42 | id: deployment
43 | uses: actions/deploy-pages@v4
44 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | release:
10 | name: Release
11 | if: ${{ github.repository_owner == 'delucis' }}
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
17 | fetch-depth: 0
18 |
19 | - uses: pnpm/action-setup@v3
20 |
21 | - uses: actions/setup-node@v4
22 | with:
23 | node-version: 20
24 | cache: pnpm
25 |
26 | - run: pnpm i
27 |
28 | - uses: changesets/action@v1
29 | with:
30 | version: pnpm run ci-version
31 | publish: pnpm run ci-publish
32 | commit: '[ci] release'
33 | title: '[ci] release'
34 | env:
35 | GITHUB_TOKEN: ${{ secrets.CHRISBOT_GITHUB_TOKEN }}
36 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules/
3 |
4 | # logs
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | pnpm-debug.log*
9 |
10 | # environment variables
11 | .env
12 | .env.production
13 |
14 | # macOS-specific files
15 | .DS_Store
16 |
17 | # build output
18 | dist/
19 |
20 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | prefer-workspace-packages=true
2 | link-workspace-packages=true
3 |
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | packages/expressive-code-color-chips/README.md
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 | # generated types
4 | .astro/
5 |
6 | # dependencies
7 | node_modules/
8 |
9 | # logs
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | pnpm-debug.log*
14 |
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Expressive Code Color Chips Docs
2 |
3 | [](https://starlight.astro.build)
4 |
5 | This directory contains the documentation website.
6 |
7 | ## 🧞 Commands
8 |
9 | In this directory, you can run these commands from a terminal:
10 |
11 | | Command | Action |
12 | | :------------------------- | :----------------------------------------------- |
13 | | `pnpm install` | Installs dependencies |
14 | | `pnpm run dev` | Starts local dev server at `localhost:4321` |
15 | | `pnpm run build` | Build your production site to `./dist/` |
16 | | `pnpm run preview` | Preview your build locally, before deploying |
17 | | `pnpm run astro ...` | Run CLI commands like `astro add`, `astro check` |
18 | | `pnpm run astro -- --help` | Get help using the Astro CLI |
19 |
--------------------------------------------------------------------------------
/docs/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { defineConfig } from 'astro/config';
3 | import starlight from '@astrojs/starlight';
4 |
5 | // https://astro.build/config
6 | export default defineConfig({
7 | site: 'https://delucis.github.io',
8 | base: '/expressive-code-color-chips',
9 | integrations: [
10 | starlight({
11 | title: 'Expressive Code Color Chips',
12 | description:
13 | 'CSS color preview plugin for Expressive Code. Display a small sample of each CSS color in your code examples.',
14 | social: {
15 | github: 'https://github.com/delucis/expressive-code-color-chips',
16 | },
17 | sidebar: ['getting-started', 'configuration'],
18 | customCss: ['./src/styles.css'],
19 | head: [
20 | {
21 | tag: 'meta',
22 | attrs: {
23 | property: 'og:image',
24 | content: 'https://delucis.github.io/expressive-code-color-chips/og.png',
25 | },
26 | },
27 | ],
28 | }),
29 | ],
30 | });
31 |
--------------------------------------------------------------------------------
/docs/ec.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineEcConfig } from '@astrojs/starlight/expressive-code';
2 | import { pluginColorChips } from 'expressive-code-color-chips';
3 |
4 | export default defineEcConfig({
5 | plugins: [pluginColorChips()],
6 | styleOverrides: {
7 | colorChips: {
8 | // borderRadius: 0,
9 | },
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@expressive-code-color-chips/docs",
3 | "type": "module",
4 | "private": true,
5 | "version": "0.0.1",
6 | "scripts": {
7 | "dev": "astro dev",
8 | "start": "astro dev",
9 | "build": "astro check && astro build",
10 | "preview": "astro preview",
11 | "astro": "astro"
12 | },
13 | "dependencies": {
14 | "@astrojs/check": "^0.9.4",
15 | "@astrojs/starlight": "^0.28.3",
16 | "astro": "^4.16.18",
17 | "expressive-code-color-chips": "*",
18 | "sharp": "^0.32.5",
19 | "starlight-package-managers": "^0.7.0",
20 | "typescript": "^5.6.3"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/public/og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/delucis/expressive-code-color-chips/71e97da90084190f3ddb01ea61edd052395f2175/docs/public/og.png
--------------------------------------------------------------------------------
/docs/src/content/config.ts:
--------------------------------------------------------------------------------
1 | import { defineCollection } from 'astro:content';
2 | import { docsSchema } from '@astrojs/starlight/schema';
3 |
4 | export const collections = {
5 | docs: defineCollection({ schema: docsSchema() }),
6 | };
7 |
--------------------------------------------------------------------------------
/docs/src/content/docs/configuration.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Configuration
3 | description: Optional configuration for the Expressive Code Color Chips plugin, including how to customize styling.
4 | ---
5 |
6 | After adding the Color Chips plugin to your Expressive Code config as shown in [the set-up guide](/expressive-code-color-chips/getting-started/), no further configuration is required.
7 |
8 | If you want to customize the appearance of the color chips, you can use style overrides.
9 |
10 | ## Styling
11 |
12 | You can provide custom styles in your Expressive Code config using the `styleOverrides.colorChips` object.
13 |
14 | For example, to display square chips instead of the default round style, set `borderRadius` to zero:
15 |
16 | ```js
17 | {
18 | plugins: [pluginColorChips()],
19 | styleOverrides: {
20 | colorChips: {
21 | borderRadius: 0,
22 | },
23 | },
24 | },
25 | ```
26 |
27 | ## Available style overrides
28 |
29 | ### `size`
30 |
31 | **Default:** `"1.2em"`
32 |
33 | The size of each color chip.
34 |
35 | ### `borderWidth`
36 |
37 | **Default:** `"1px"`
38 |
39 | The width of the border around each color chip.
40 |
41 | ### `borderRadius`
42 |
43 | **Default:** `"50%"`
44 |
45 | The roundness of each color chip.
46 |
47 | ### `borderColor`
48 |
49 | **Default:** `({ theme }) => theme.fg`
50 |
51 | The color of the border around each chip.
52 |
53 | ### `transparencyShadeOne`
54 |
55 | **Default:** `["#777", "#fff"]`
56 |
57 | The first of two shades used in the checkerboard background for transparent colors.
58 |
59 | ### `transparencyShadeTwo`
60 |
61 | **Default:** `["#000", "#bbb"]`
62 |
63 | The second of two shades used in the checkerboard background for transparent colors.
64 |
--------------------------------------------------------------------------------
/docs/src/content/docs/getting-started.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | description: How to install and use the Expressive Code Color Chips plugin in your website.
4 | ---
5 |
6 | The Color Chips plugin extends [Expressive Code](https://expressive-code.com/) to display a preview “chip” for colors in CSS code blocks.
7 | This guide will show you how to add it to your website.
8 |
9 | ## Set-up
10 |
11 | import { Steps, Tabs, TabItem } from '@astrojs/starlight/components';
12 | import { PackageManagers } from 'starlight-package-managers';
13 |
14 |
15 |
16 | 1. Install the `expressive-code-color-chips` dependency using your preferred package manager:
17 |
18 |
19 |
20 | 2. Add the plugin to your site’s Expressive Code configuration:
21 |
22 |
23 |
24 |
25 |
26 | ```js title="astro.config.mjs" ins={3,8}
27 | import { defineConfig } from 'astro/config';
28 | import astroExpressiveCode from 'astro-expressive-code';
29 | import { pluginColorChips } from 'expressive-code-color-chips';
30 |
31 | export default defineConfig({
32 | integrations: [
33 | astroExpressiveCode({
34 | plugins: [pluginColorChips()],
35 | }),
36 | ],
37 | });
38 | ```
39 |
40 |
41 |
42 |
43 |
44 | ```js title="astro.config.mjs" ins={3,10}
45 | import { defineConfig } from 'astro/config';
46 | import starlight from '@astrojs/starlight';
47 | import { pluginColorChips } from 'expressive-code-color-chips';
48 |
49 | export default defineConfig({
50 | integrations: [
51 | starlight({
52 | title: 'My Starlight site',
53 | expressiveCode: {
54 | plugins: [pluginColorChips()],
55 | },
56 | }),
57 | ],
58 | });
59 | ```
60 |
61 |
62 |
63 |
64 |
65 | ```js title="next.config.mjs" ins={3,7}
66 | import createMDX from '@next/mdx';
67 | import rehypeExpressiveCode from 'rehype-expressive-code';
68 | import { pluginColorChips } from 'expressive-code-color-chips';
69 |
70 | /** @type {import('rehype-expressive-code').RehypeExpressiveCodeOptions} */
71 | const rehypeExpressiveCodeOptions = {
72 | plugins: [pluginColorChips()],
73 | };
74 |
75 | /** @type {import('next').NextConfig} */
76 | const nextConfig = {
77 | reactStrictMode: true,
78 | pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
79 | };
80 |
81 | const withMDX = createMDX({
82 | extension: /\.mdx?$/,
83 | options: {
84 | remarkPlugins: [],
85 | rehypePlugins: [
86 | // The nested array structure is required to pass options
87 | // to a rehype plugin
88 | [rehypeExpressiveCode, rehypeExpressiveCodeOptions],
89 | ],
90 | },
91 | });
92 |
93 | export default withMDX(nextConfig);
94 | ```
95 |
96 |
97 |
98 |
99 |
100 | ```js title="ec.config.mjs" ins={1,4}
101 | import { pluginColorChips } from 'expressive-code-color-chips';
102 |
103 | export default {
104 | plugins: [pluginColorChips()],
105 | };
106 | ```
107 |
108 |
109 |
110 |
111 |
112 | 3. That’s it! Colors in CSS code blocks will now be annotated with a small preview.
113 |
114 |
115 |
116 | ## Usage
117 |
118 | There is no specific syntax for this plugin.
119 | It detects CSS color syntax in code blocks tagged with `css` and annotates all valid colors it finds.
120 |
121 | For example the following Markdown code:
122 |
123 | ````md
124 | ```css
125 | .example {
126 | color: goldenrod;
127 | }
128 | ```
129 | ````
130 |
131 | Renders a code block with a chip next to the color `goldenrod`:
132 |
133 | ```css
134 | .example {
135 | color: goldenrod;
136 | }
137 | ```
138 |
139 | In addition to `css`, this plugin also runs for `scss`, `sass`, `less`, and `stylus` code blocks.
140 | For example:
141 |
142 | ````md
143 | ```scss
144 | $primary: fuchsia;
145 | ```
146 | ````
147 |
148 | Which renders as:
149 |
150 | ```scss
151 | $primary: fuchsia;
152 | ```
153 |
154 | ## Support
155 |
156 | This is open-source software.
157 | If you run into any bugs, please [report issues on GitHub](https://github.com/delucis/expressive-code-color-chips/issues).
158 |
--------------------------------------------------------------------------------
/docs/src/content/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Expressive Code Color Chips
3 | description: Add color previews to your CSS code examples with the Expressive Code Color Chips plugin.
4 | template: splash
5 | hero:
6 | tagline: A plugin to add color previews to your CSS code examples
7 | image:
8 | html: 🎨
9 | actions:
10 | - text: Get started
11 | link: /expressive-code-color-chips/getting-started/
12 | icon: right-arrow
13 | - text: Star on GitHub
14 | link: https://github.com/delucis/expressive-code-color-chips
15 | icon: star
16 | variant: minimal
17 | head:
18 | - tag: style
19 | content: >-
20 | .hero-html {
21 | --size: min(10rem, calc(5rem + 6vw));
22 | --blur: calc(var(--size) / 3.5);
23 | --x-shift: calc(var(--size) / 3);
24 | --y-shift: calc(var(--size) / 5);
25 | font-size: var(--size);
26 | line-height: var(--size);
27 | justify-content: center;
28 | filter:
29 | drop-shadow(0 0 var(--blur) var(--sl-color-blue))
30 | drop-shadow(calc(-1 * var(--x-shift)) var(--y-shift) var(--blur) var(--sl-color-purple))
31 | drop-shadow(var(--x-shift) calc(-1 * var(--y-shift)) var(--blur) var(--sl-color-green));
32 | z-index: -1;
33 | }
34 | [data-has-hero] header {
35 | border-color: transparent;
36 | background-color: transparent;
37 | backdrop-filter: blur(1rem);
38 | }
39 | .hero h1 { text-wrap: balance }
40 | ---
41 |
42 | ```css title="example.css"
43 | .chips {
44 | /* CSS named colors */
45 | color: red;
46 | /* Hexadecimal colors */
47 | background-color: #fff;
48 | /* HSL color functions */
49 | border-color: hsl(0, 0%, 0%);
50 | /* System colors */
51 | outline-color: SelectedItem;
52 | /* Transparent colors */
53 | background: linear-gradient(rgba(0, 0, 255, 0.25), rgba(0, 0, 255, 0.75));
54 | /* And more… */
55 | --more: oklch(70% 0.1 72);
56 | }
57 | ```
58 |
--------------------------------------------------------------------------------
/docs/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/docs/src/styles.css:
--------------------------------------------------------------------------------
1 | /* Dark mode colors. */
2 | :root {
3 | --sl-color-accent-low: #242424;
4 | --sl-color-accent: #6a6a6a;
5 | --sl-color-accent-high: #fff;
6 | --sl-color-white: #ffffff;
7 | --sl-color-gray-1: #eeeeee;
8 | --sl-color-gray-2: #c2c2c2;
9 | --sl-color-gray-3: #8b8b8b;
10 | --sl-color-gray-4: #585858;
11 | --sl-color-gray-5: #383838;
12 | --sl-color-gray-6: #272727;
13 | --sl-color-black: #181818;
14 | }
15 | /* Light mode colors. */
16 | :root[data-theme='light'] {
17 | --sl-color-accent-low: #d7d7d7;
18 | --sl-color-accent: #181818;
19 | --sl-color-accent-high: #000;
20 | --sl-color-white: #181818;
21 | --sl-color-gray-1: #272727;
22 | --sl-color-gray-2: #383838;
23 | --sl-color-gray-3: #585858;
24 | --sl-color-gray-4: #8b8b8b;
25 | --sl-color-gray-5: #c2c2c2;
26 | --sl-color-gray-6: #eeeeee;
27 | --sl-color-gray-7: #f6f6f6;
28 | --sl-color-black: #ffffff;
29 | }
30 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "exclude": ["dist/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@expressive-code-color-chips/root",
3 | "version": "0.0.0",
4 | "private": true,
5 | "author": "delucis",
6 | "license": "MIT",
7 | "packageManager": "pnpm@9.12.1",
8 | "devDependencies": {
9 | "tsup": "^8.3.5"
10 | },
11 | "dependencies": {
12 | "@changesets/changelog-github": "^0.5.0",
13 | "@changesets/cli": "^2.27.9"
14 | },
15 | "scripts": {
16 | "build": "pnpm -F expressive-code-color-chips build",
17 | "build:docs": "pnpm -F @expressive-code-color-chips/docs build",
18 | "ci-version": "changeset version && pnpm install --no-frozen-lockfile",
19 | "ci-publish": "pnpm build && changeset publish"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/expressive-code-color-chips/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # expressive-code-color-chips
2 |
3 | ## 0.1.2
4 |
5 | ### Patch Changes
6 |
7 | - [`75369fd`](https://github.com/delucis/expressive-code-color-chips/commit/75369fda4077abf4dff9a25d49adad443ccd320e) Thanks [@delucis](https://github.com/delucis)! - Updates `package.json` metadata
8 |
9 | ## 0.1.1
10 |
11 | ### Patch Changes
12 |
13 | - [`8cb8982`](https://github.com/delucis/expressive-code-color-chips/commit/8cb89821731d98ecaa6d5fffc91963b506f68edb) Thanks [@delucis](https://github.com/delucis)! - Avoids annotating named colors inside CSS comments as this can lead to false positives
14 |
15 | ## 0.1.0
16 |
17 | ### Minor Changes
18 |
19 | - [`90f3386`](https://github.com/delucis/expressive-code-color-chips/commit/90f3386ac1077ed415533d527acc52c68597b785) Thanks [@delucis](https://github.com/delucis)! - Initial release
20 |
--------------------------------------------------------------------------------
/packages/expressive-code-color-chips/README.md:
--------------------------------------------------------------------------------
1 | # Expressive Code Color Chips
2 |
3 | An Expressive Code plugin to add color previews to your syntax highlighted CSS code examples.
4 |
5 | 
6 |
7 | ## Documentation
8 |
9 | [Read the full documentation →](https://delucis.github.io/expressive-code-color-chips/)
10 |
11 | ## License
12 |
13 | MIT
14 |
--------------------------------------------------------------------------------
/packages/expressive-code-color-chips/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "expressive-code-color-chips",
3 | "description": "CSS color preview plugin for Expressive Code. Display a small sample of each CSS color in your syntax highlighted code examples.",
4 | "version": "0.1.2",
5 | "keywords": [
6 | "expressive-code",
7 | "withastro",
8 | "syntax-highlighting"
9 | ],
10 | "author": "Chris Swithinbank ",
11 | "homepage": "https://delucis.github.io/expressive-code-color-chips/",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/delucis/expressive-code-color-chips",
15 | "directory": "packages/expressive-code-color-chips"
16 | },
17 | "bugs": "https://github.com/delucis/expressive-code-color-chips/issues",
18 | "license": "MIT",
19 | "type": "module",
20 | "main": "./dist/index.js",
21 | "module": "./dist/index.js",
22 | "exports": {
23 | "types": "./dist/index.d.ts",
24 | "default": "./dist/index.js"
25 | },
26 | "types": "./dist/index.d.ts",
27 | "files": [
28 | "dist"
29 | ],
30 | "scripts": {
31 | "build": "tsup ./src/index.ts --format esm --dts --sourcemap --clean",
32 | "watch": "pnpm build --watch src"
33 | },
34 | "devDependencies": {
35 | "@expressive-code/core": "^0.37.1"
36 | },
37 | "peerDependencies": {
38 | "@expressive-code/core": "^0.37.1"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/expressive-code-color-chips/src/colorRegEx.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Match all CSS color syntaxes, including named and system colors.
3 | *
4 | * Based on [`Kyza/color-regex`](https://github.com/Kyza/color-regex/blob/trunk/index.reg),
5 | * but modified to avoid matching `currentColor`, which is contextual so can’t be displayed.
6 | *
7 | * MIT License
8 | *
9 | * Copyright (c) 2023 Kyza
10 | *
11 | * Permission is hereby granted, free of charge, to any person obtaining a copy
12 | * of this software and associated documentation files (the "Software"), to deal
13 | * in the Software without restriction, including without limitation the rights
14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | * copies of the Software, and to permit persons to whom the Software is
16 | * furnished to do so, subject to the following conditions:
17 | *
18 | * The above copyright notice and this permission notice shall be included in all
19 | * copies or substantial portions of the Software.
20 | *
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | * SOFTWARE.
28 | */
29 | export const colors = {
30 | /** Match all CSS color syntaxes, excluding named and system colors. */
31 | programmatic:
32 | /(#)(?:([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})?|([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])?)|(rgb|rgba)\((?:\s*(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)\s*,\s*(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)\s*,\s*(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)(?:\s*,\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*|\s*(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)\s+(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)\s+(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)\s*|\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s*,\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s*,\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:\s*,\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*|\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s*|\s*(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)\s+(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)\s+(0*(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-4]?|[6-9])?|[3-9][0-9]?)(?:\.[0-9]+)?|255(?:\.0+)?|\.[0-9]+)(?:\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*|\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*)\)|(hsl|hsla)\((?:\s*(-?[0-9]+(?:\.[0-9]+)?(?:deg|rad|grad|turn)?)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*|\s*(-?[0-9]+(?:\.[0-9]+)?(?:deg|rad|grad|turn)?)\s*,\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s*,\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:\s*,\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*|\s*(-?[0-9]+(?:\.[0-9]+)?(?:deg|rad|grad|turn)?)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s*)\)|(hwb)\(\s*(-?[0-9]+(?:\.[0-9]+)?(?:deg|rad|grad|turn)?)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:(?:\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*)?\)|(lab|oklab)\(\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?)\s+(-?(?:0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|(?:0|1(?:[0-1][0-9]?|2[0-4]?|[3-9])?|[2-9][0-9]?)(?:\.[0-9]+)?|125(?:\.0+)?))\s+(-?(?:0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|(?:0|1(?:[0-1][0-9]?|2[0-4]?|[3-9])?|[2-9][0-9]?)(?:\.[0-9]+)?|125(?:\.0+)?))\s*(?:(?:\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*)?\)|(lch|oklch)\(\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?)\s+(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|(?:0|1(?:[0-4][0-9]?|[5-9])?|[2-9][0-9]?)(?:\.[0-9]+)?|150(?:\.0+)?)\s+(-?[0-9]+(?:\.[0-9]+)?(?:deg|rad|grad|turn)?)\s*(?:(?:\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*)?\)|(color)\((?:(srgb|srgb-linear|display-p3|a98-rgb|prophoto-rgb|rec2020)(?:\s+|\s*,\s*)(0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+|0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:\s+|\s*,\s*)(0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+|0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:\s+|\s*,\s*)(0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+|0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%)(?:(?:\s+\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*)?|(xyz|xyz-d50|xyz-d65)(?:\s+|\s*,\s*)(-?[0-9]+(?:\.[0-9]+)?%?)(?:\s+|\s*,\s*)(-?[0-9]+(?:\.[0-9]+)?%?)(?:\s+|\s*,\s*)(-?[0-9]+(?:\.[0-9]+)?%?)(?:(?:\s+\s*(?:\/)\s*(0*(?:(?:0|[1-9][0-9]?)(?:\.[0-9]+)?|100(?:\.0+)?|\.[0-9]+)%|0*0*(?:\.[0-9]+)?|1(?:\.0+)?|\.[0-9]+))?\s*)?)\)/gi,
33 | /** Match CSS named and system colors. */
34 | named:
35 | /(yellowgreen|yellow|whitesmoke|white|wheat|VisitedText|violet|turquoise|transparent|tomato|thistle|teal|tan|steelblue|springgreen|snow|slategrey|slategray|slateblue|skyblue|silver|sienna|SelectedItemText|SelectedItem|seashell|seagreen|sandybrown|salmon|saddlebrown|royalblue|rosybrown|red|rebeccapurple|purple|powderblue|plum|pink|peru|peachpuff|papayawhip|palevioletred|paleturquoise|palegreen|palegoldenrod|orchid|orangered|orange|olivedrab|olive|oldlace|navy|navajowhite|moccasin|mistyrose|mintcream|midnightblue|mediumvioletred|mediumturquoise|mediumspringgreen|mediumslateblue|mediumseagreen|mediumpurple|mediumorchid|mediumblue|mediumaquamarine|maroon|MarkText|Mark|magenta|LinkText|linen|limegreen|lime|lightyellow|lightsteelblue|lightslategrey|lightslategray|lightskyblue|lightseagreen|lightsalmon|lightpink|lightgrey|lightgreen|lightgray|lightgoldenrodyellow|lightcyan|lightcoral|lightblue|lemonchiffon|lawngreen|lavenderblush|lavender|khaki|ivory|indigo|indianred|hotpink|honeydew|HighlightText|Highlight|grey|greenyellow|green|GrayText|gray|goldenrod|gold|ghostwhite|gainsboro|fuchsia|forestgreen|floralwhite|firebrick|FieldText|Field|dodgerblue|dimgrey|dimgray|deepskyblue|deeppink|darkviolet|darkturquoise|darkslategrey|darkslategray|darkslateblue|darkseagreen|darksalmon|darkred|darkorchid|darkorange|darkolivegreen|darkmagenta|darkkhaki|darkgrey|darkgreen|darkgray|darkgoldenrod|darkcyan|darkblue|cyan|crimson|cornsilk|cornflowerblue|coral|chocolate|chartreuse|CanvasText|Canvas|cadetblue|ButtonText|ButtonFace|ButtonBorder|burlywood|brown|blueviolet|blue|blanchedalmond|black|bisque|beige|azure|aquamarine|aqua|antiquewhite|aliceblue|ActiveText|AccentColorText|AccentColor)/gi,
36 | };
37 |
--------------------------------------------------------------------------------
/packages/expressive-code-color-chips/src/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type AnnotationBaseOptions,
3 | type AnnotationRenderOptions,
4 | definePlugin,
5 | ExpressiveCodeAnnotation,
6 | ExpressiveCodeLine,
7 | PluginStyleSettings,
8 | } from '@expressive-code/core';
9 | import { h } from '@expressive-code/core/hast';
10 | import { colors } from './colorRegEx';
11 |
12 | const chipClass = 'ec-css-color-chip';
13 | const localColorVariable = '--ec-css-color-chip';
14 | /** Language tags where CSS color annotations should apply. */
15 | const cssDialects = new Set(['css', 'scss', 'sass', 'less', 'stylus']);
16 |
17 | /** Adds an annotation to a CSS color, which will display that color as a chip next to it. */
18 | class CssColorAnnotation extends ExpressiveCodeAnnotation {
19 | /** CSS color being annotated */
20 | color: string;
21 |
22 | constructor(opts: AnnotationBaseOptions & { color: string }) {
23 | super(opts);
24 | this.color = opts.color;
25 | }
26 |
27 | render({ nodesToTransform }: AnnotationRenderOptions) {
28 | return nodesToTransform.map((node) => {
29 | return h(`span.${chipClass}`, { style: `${localColorVariable}: ${this.color}` }, node);
30 | });
31 | }
32 | }
33 |
34 | /** Match all CSS comments in a line, including trailing unclosed comments or leading comments. */
35 | const commentRegEx = /(?:^[^*]*\*\/)?\/\*[^*]*(?:\*\/)?/gi;
36 |
37 | /**
38 | * Process a code block line and annotate any colors found.
39 | */
40 | function annotateLine(line: ExpressiveCodeLine) {
41 | /** An array of character positions that are inside comments. */
42 | const commentPositions = [...line.text.matchAll(commentRegEx)]
43 | // Convert each match to an array of indexes for characters in that range.
44 | .flatMap((match) => Array.from(match[0]).map((_, i) => match.index + i));
45 | [
46 | // Colors expressed with explicit color syntax.
47 | ...line.text.matchAll(colors.programmatic),
48 | // Colors expressed with a named keyword, e.g. “blue” or “Canvas”.
49 | ...[...line.text.matchAll(colors.named)].filter(
50 | (match) => !commentPositions.includes(match.index)
51 | ),
52 | ]
53 | // Sort matches in reverse order by start position in the line (i.e. last match first).
54 | .sort((a, b) => b.index - a.index)
55 | // Annotate each match.
56 | .forEach((match) => {
57 | const color = match[0];
58 | const columnStart = match.index;
59 | const columnEnd = columnStart + color.length;
60 | line.addAnnotation(
61 | new CssColorAnnotation({
62 | color,
63 | inlineRange: { columnStart, columnEnd },
64 | })
65 | );
66 | });
67 | }
68 |
69 | /**
70 | * Expressive Code plugin that adds a small preview of each CSS color in your code examples.
71 | */
72 | export function pluginColorChips() {
73 | return definePlugin({
74 | name: 'ColorChips',
75 | hooks: {
76 | postprocessAnalyzedCode({ codeBlock }) {
77 | if (cssDialects.has(codeBlock.language)) {
78 | codeBlock.getLines().forEach((line) => annotateLine(line));
79 | }
80 | },
81 | },
82 | styleSettings: new PluginStyleSettings({
83 | defaultValues: {
84 | colorChips: {
85 | size: '1.2em',
86 | borderWidth: '1px',
87 | borderRadius: '50%',
88 | borderColor: ({ theme }) => theme.fg,
89 | transparencyShadeOne: ['#777', '#fff'],
90 | transparencyShadeTwo: ['#000', '#bbb'],
91 | },
92 | },
93 | }),
94 | baseStyles({ cssVar }) {
95 | const a = cssVar('colorChips.transparencyShadeOne');
96 | const b = cssVar('colorChips.transparencyShadeTwo');
97 | return `
98 | .${chipClass}::before {
99 | content: "";
100 | display: inline-block;
101 | box-sizing: border-box;
102 | width: ${cssVar('colorChips.size')};
103 | height: ${cssVar('colorChips.size')};
104 | margin-inline-end: 0.25em;
105 | vertical-align: text-bottom;
106 | background:
107 | linear-gradient(var(${localColorVariable}), var(${localColorVariable})),
108 | conic-gradient(${b} 25%, ${a} 25%, ${a} 50%, ${b} 50%, ${b} 75%, ${a} 75%);
109 | border-width: ${cssVar('colorChips.borderWidth')};
110 | border-style: solid;
111 | border-color: ${cssVar('colorChips.borderColor')};
112 | border-radius: ${cssVar('colorChips.borderRadius')};
113 | }
114 | `;
115 | },
116 | });
117 | }
118 |
119 | interface ColorChipsStyleSettings {
120 | /** The size of each color chip. Default: `"1.2em"` */
121 | size: string;
122 | /** The width of the border around each color chip. Default: `"1px"` */
123 | borderWidth: string;
124 | /** The roundness of each color chip. Default: `"50%"` */
125 | borderRadius: string;
126 | /** The color of the border around each chip. Default: `({ theme }) => theme.fg` */
127 | borderColor: string;
128 | /** The first of two shades used in the checkerboard background for transparent colors. Default: `["#777", "#fff"]` */
129 | transparencyShadeOne: string;
130 | /** The second of two shades used in the checkerboard background for transparent colors. Default: `["#000", "#bbb"]` */
131 | transparencyShadeTwo: string;
132 | }
133 |
134 | declare module '@expressive-code/core' {
135 | export interface StyleSettings {
136 | /** Style overrides for the CSS color chips plugin. */
137 | colorChips: ColorChipsStyleSettings;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/packages/expressive-code-color-chips/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src/**/*.ts"],
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "declaration": true,
6 | "declarationMap": true,
7 | "emitDeclarationOnly": true,
8 | "esModuleInterop": true,
9 | "exactOptionalPropertyTypes": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "resolveJsonModule": true,
12 | "skipLibCheck": true,
13 | "strict": true,
14 | "moduleResolution": "node",
15 | "module": "ESNext",
16 | "target": "ESNext",
17 | "outDir": "./dist"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/**/*'
3 | - 'docs'
4 |
5 |
--------------------------------------------------------------------------------