@astrojs/starlight
Version:
Build beautiful, high-performance documentation websites with Astro
106 lines (88 loc) • 4.51 kB
text/typescript
import { ExpressiveCodeTheme, type ThemeObjectOrShikiThemeName } from 'astro-expressive-code';
import nightOwlDark from './themes/night-owl-dark.jsonc?raw';
import nightOwlLight from './themes/night-owl-light.jsonc?raw';
export type BundledThemeName = 'starlight-dark' | 'starlight-light';
export type ThemeObjectOrBundledThemeName = ThemeObjectOrShikiThemeName | BundledThemeName;
/**
* Converts the Starlight `themes` config option into a format understood by Expressive Code,
* loading any bundled themes and using the Starlight defaults if no themes were provided.
*/
export function preprocessThemes(
themes: ThemeObjectOrBundledThemeName[] | undefined
): ThemeObjectOrShikiThemeName[] {
// Try to gracefully handle cases where the user forgot to use an array in the config
themes = themes && !Array.isArray(themes) ? [themes] : themes;
// If no themes were provided, use our bundled default themes
if (!themes || !themes.length) themes = ['starlight-dark', 'starlight-light'];
return themes.map((theme) => {
// If the current entry is the name of a bundled theme, load it
if (theme === 'starlight-dark' || theme === 'starlight-light') {
const bundledTheme = theme === 'starlight-dark' ? nightOwlDark : nightOwlLight;
return customizeBundledTheme(ExpressiveCodeTheme.fromJSONString(bundledTheme));
}
// Otherwise, just pass it through
return theme;
});
}
/**
* Customizes some settings of the bundled theme to make it fit better with Starlight.
*/
function customizeBundledTheme(theme: ExpressiveCodeTheme) {
theme.colors['titleBar.border'] = theme.colors['tab.activeBackground'];
theme.colors['editorGroupHeader.tabsBorder'] = theme.colors['tab.activeBackground'];
// Add underline font style to link syntax highlighting tokens
// to match the new GitHub theme link style
theme.settings.forEach((s) => {
if (s.name?.includes('Link')) s.settings.fontStyle = 'underline';
});
return theme;
}
/**
* Modifies the given theme by applying Starlight's CSS variables to the colors of UI elements
* (backgrounds, buttons, shadows etc.). This ensures that code blocks match the site's theme.
*/
export function applyStarlightUiThemeColors(theme: ExpressiveCodeTheme) {
const isDark = theme.type === 'dark';
const neutralMinimal = isDark ? '#ffffff17' : '#0000001a';
const neutralDimmed = isDark ? '#ffffff40' : '#00000055';
// Make borders slightly transparent
const borderColor = 'color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)';
theme.colors['titleBar.border'] = borderColor;
theme.colors['editorGroupHeader.tabsBorder'] = borderColor;
// Use the same color for terminal title bar background and editor tab bar background
const backgroundColor = isDark ? 'var(--sl-color-black)' : 'var(--sl-color-gray-6)';
theme.colors['titleBar.activeBackground'] = backgroundColor;
theme.colors['editorGroupHeader.tabsBackground'] = backgroundColor;
// Use the same color for terminal titles and tab titles
theme.colors['titleBar.activeForeground'] = 'var(--sl-color-text)';
theme.colors['tab.activeForeground'] = 'var(--sl-color-text)';
// Set tab border colors
const activeBorderColor = isDark ? 'var(--sl-color-accent-high)' : 'var(--sl-color-accent)';
theme.colors['tab.activeBorder'] = 'transparent';
theme.colors['tab.activeBorderTop'] = activeBorderColor;
// Use neutral colors for scrollbars
theme.colors['scrollbarSlider.background'] = neutralMinimal;
theme.colors['scrollbarSlider.hoverBackground'] = neutralDimmed;
// Set theme `bg` color property for contrast calculations
theme.bg = isDark ? '#23262f' : '#f6f7f9';
theme.colors['editor.background'] = theme.bg;
// Set actual background color to the appropriate Starlight CSS variable
const editorBackgroundColor = isDark ? 'var(--sl-color-gray-6)' : 'var(--sl-color-gray-7)';
theme.styleOverrides.frames = {
// Use the same color for editor background, terminal background and active tab background
editorBackground: editorBackgroundColor,
terminalBackground: editorBackgroundColor,
editorActiveTabBackground: editorBackgroundColor,
terminalTitlebarDotsForeground: borderColor,
terminalTitlebarDotsOpacity: '0.75',
inlineButtonForeground: 'var(--sl-color-text)',
frameBoxShadowCssValue: 'none',
};
// Use neutral, semi-transparent colors for default text markers
// to avoid conflicts with the user's chosen background color
theme.styleOverrides.textMarkers = {
markBackground: neutralMinimal,
markBorderColor: neutralDimmed,
};
return theme;
}