diff --git a/.changeset/thick-buckets-agree.md b/.changeset/thick-buckets-agree.md new file mode 100644 index 0000000..0c0ee4e --- /dev/null +++ b/.changeset/thick-buckets-agree.md @@ -0,0 +1,8 @@ +--- +"ec-lang-logo": minor +--- + +Allows for automatic color change from `#fff` to `#000` when the code block is shown with a light theme. +This is achieved by looking at the `html[data-theme]` value. + +Also adds another `color` option: `theme` takes the standard code foreground color and applies it to the language logo. diff --git a/README.md b/README.md new file mode 100644 index 0000000..80cf856 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Language Logo Plugin For Expressive Code + +A plugin for Expressive Code that adds language logos (coming from [Simple Icons](https://simpleicons.org)) to code blocks. + +## Examples + +example code block with mono color +example code block with the original brand color +example code block with a light theme + +For more examples, see [my blog post](http://blog.dertimonius.dev/posts/til-38). + +## Installation + +```bash +npm install ec-lang-logo +# pnpm install ec-lang-logo +# bun install ec-lang-logo +# yarn add ec-lang-logo +``` + +## Usage + +Add the plugin to your Expressive Code configuration: + +```js +import { defineConfig } from 'astro/config'; +import { pluginLanguageLogo } from 'ec-lang-logo'; + +export default defineConfig({ + integrations: [ + starlight({ + expressiveCode: { + plugins: [pluginLanguageLogo()], + }, + }), + ], +}); +``` + +## Configuration + +The plugin accepts an optional configuration object: + +```ts +pluginLanguageLogo({ + color: 'mono', // 'mono' | 'original' | 'theme' | '#hexcolor' + excludedLangs: [], // Array of language identifiers to exclude +}) +``` + +### Options + +- `color`: Controls the badge color + - `'mono'`: Adapts to theme (white for dark, black for light) + - `'original'`: Uses the official language brand color + - `'theme'`: Takes the code foreground color of the currently active theme + - `'#hexcolor'`: Custom hex color (e.g., `'#ff0000'`) +- `excludedLangs`: Array of language identifiers where badges should not appear + +## Per-Block Customization + +You can override settings for individual code blocks using meta attributes: + +```md +```js badge-color="#ff0000" +// Custom red badge +``` + +```md +```js badge-color="original" +// Shows the official brand color +``` + +```md +```ts hide-badge +// No badge on this block +``` + +``` + +### Available Meta Attributes + +- `badge-color="value"`: Override the badge color for this block (takes the same values as the overall options) +- `hide-badge`: Hide the badge for this block + +## Theme Support + +The plugin automatically adapts mono-colored badges to your site's theme. Ensure your theme switcher uses `data-theme="light"` or `data-theme="dark"` on the `html` element. + diff --git a/assets/light.png b/assets/light.png new file mode 100644 index 0000000..8003b23 Binary files /dev/null and b/assets/light.png differ diff --git a/assets/original.png b/assets/original.png new file mode 100644 index 0000000..cda13d1 Binary files /dev/null and b/assets/original.png differ diff --git a/assets/plain.png b/assets/plain.png new file mode 100644 index 0000000..1d51172 Binary files /dev/null and b/assets/plain.png differ diff --git a/src/index.ts b/src/index.ts index 9de43bd..2873092 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,8 @@ const LANGUAGE_MAP = { yml: { icon: icons.siYaml }, yaml: { icon: icons.siYaml }, json: { icon: icons.siJson }, + graphql: { icon: icons.siGraphql }, + gql: { icon: icons.siGraphql }, html: { icon: icons.siHtml5 }, css: { icon: icons.siCss }, @@ -47,8 +49,10 @@ const LANGUAGE_MAP = { type Language = keyof typeof LANGUAGE_MAP; +type Color = 'mono' | 'original' | 'theme' | (`#${string}` & {}); + interface PluginOptions { - color: 'mono' | 'original' | (`#${string}` & {}); + color: Color; excludedLangs: Language[]; } @@ -63,6 +67,14 @@ export function pluginLanguageLogo(config?: PluginOptions) { return definePlugin({ name: 'Language Logo', baseStyles: ` + html[data-theme=light] { + --ec-lang-logo: #000; + } + + html[data-theme=dark] { + --ec-lang-logo: #eee; + } + .ec-lang-logo { position: absolute; width: 48px; @@ -77,7 +89,7 @@ export function pluginLanguageLogo(config?: PluginOptions) { pointer-events: none; z-index: 10; } - [data-small=true] { + [data-ec-logo-small=true] { width: 32px; height: 32px; bottom: 0.8rem; @@ -106,16 +118,17 @@ export function pluginLanguageLogo(config?: PluginOptions) { if (!foundLang) { return; } - const loc = codeBlock.getLines().length; const icon = foundLang.icon; - const color = colorMatch ? colorMatch[1] || colorMatch[2] : opts.color; + const color = colorMatch + ? ((colorMatch[1] || colorMatch[2]) as Color) + : opts.color; const logoSvg = h( 'div', { class: 'ec-lang-logo', - 'data-small': loc === 1 ? 'true' : undefined, + 'data-ec-logo-small': loc === 1 ? 'true' : undefined, }, [ h( @@ -125,12 +138,7 @@ export function pluginLanguageLogo(config?: PluginOptions) { viewBox: '0 0 24 24', width: loc === 1 ? '24px' : '32px', height: loc === 1 ? '24px' : '32px', - fill: - color === 'mono' - ? '#fff' - : color === 'original' - ? `#${icon.hex}` - : color, + fill: getFillColor(color, icon.hex), }, [ h('title', { innerHtml: icon.title }), @@ -147,3 +155,16 @@ export function pluginLanguageLogo(config?: PluginOptions) { }, }); } + +function getFillColor(color: Color, iconHex: string): string { + if (color === 'mono') { + return 'var(--ec-lang-logo)'; + } + if (color === 'theme') { + return 'var(--ec-codeFg)'; + } + if (color === 'original') { + return `#${iconHex}`; + } + return color; +}