Skip to content

Commit 6497f09

Browse files
authored
Add preventUnitlessValues property to PluginStyleSettings (#320)
1 parent 0f33477 commit 6497f09

File tree

9 files changed

+55
-0
lines changed

9 files changed

+55
-0
lines changed

.changeset/famous-drinks-taste.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@expressive-code/plugin-collapsible-sections': minor
3+
'@expressive-code/plugin-text-markers': minor
4+
'@expressive-code/plugin-frames': minor
5+
---
6+
7+
Uses the new `preventUnitlessValues` property of `PluginStyleSettings` to make style calculations in the plugins "Collapsible Sections", "Frames" and "Text Markers" more robust.

.changeset/heavy-jokes-doubt.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@expressive-code/core': minor
3+
'astro-expressive-code': minor
4+
'expressive-code': minor
5+
'rehype-expressive-code': minor
6+
---
7+
8+
Adds new `preventUnitlessValues` property to `PluginStyleSettings`. Thank you @RantingHuman!
9+
10+
Plugins can set this property to an array of style setting paths to prevent unitless values for specific style settings. If the user passes a unitless value to one of these settings, the engine will automatically add `px` to the value. This is recommended for settings used in CSS calculations which would otherwise break if a unitless value is passed.

docs/src/content/docs/reference/plugin-api.mdx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ If CSS variables should not be generated for some of your style settings, you ca
8585
If you want to provide descriptive names for your style settings, but keep the generated CSS variable names short, you can pass an array of search and replace string pairs to the
8686
`cssVarReplacements` property of the object passed to the constructor. The replacements will be applied to all generated CSS variable names.
8787

88+
If you want to prevent unitless values for specific style settings (e.g. because you intend to use them in CSS calculations), you can pass an array of style setting paths to the
89+
`preventUnitlessValues` property of the object passed to the constructor. If the user passes a unitless value to one of these settings, the engine will automatically add `px` to the value.
90+
8891
### Example
8992

9093
```ts
@@ -137,6 +140,7 @@ framesStyleSettings.defaultValues.frames.titleBarForeground // ({ theme }) => th
137140
| `options.defaultValues` | `Partial`\<[`UnresolvedStyleSettings`](/reference/plugin-api/#unresolvedstylesettings)\> |
138141
| `options.cssVarExclusions`? | [`StyleSettingPath`](/reference/plugin-api/#stylesettingpath)[] |
139142
| `options.cssVarReplacements`? | \[`string`, `string`\][] |
143+
| `options.preventUnitlessValues`? | [`StyleSettingPath`](/reference/plugin-api/#stylesettingpath)[] |
140144

141145
### Properties
142146

@@ -158,6 +162,12 @@ framesStyleSettings.defaultValues.frames.titleBarForeground // ({ theme }) => th
158162
- Type: **readonly** `Partial`\<[`UnresolvedStyleSettings`](/reference/plugin-api/#unresolvedstylesettings)\>
159163
</PropertySignature>
160164

165+
#### preventUnitlessValues
166+
167+
<PropertySignature>
168+
- Type: **readonly** [`StyleSettingPath`](/reference/plugin-api/#stylesettingpath)[]
169+
</PropertySignature>
170+
161171
## AttachedPluginData
162172

163173
A class that allows plugins to attach custom data to objects like code blocks, and to optionally allow external access to this data in a type-safe manner.

packages/@expressive-code/core/src/common/plugin-style-settings.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ import { UnresolvedStyleSettings, StyleSettingPath } from './style-settings'
2727
* `cssVarReplacements` property of the object passed to the constructor. The replacements
2828
* will be applied to all generated CSS variable names.
2929
*
30+
* If you want to prevent unitless values for specific style settings (e.g. because you intend
31+
* to use them in CSS calculations), you can pass an array of style setting paths to the
32+
* `preventUnitlessValues` property of the object passed to the constructor. If the user passes
33+
* a unitless value to one of these settings, the engine will automatically add `px` to the value.
34+
*
3035
* @example
3136
* // When using TypeScript: Declare the types of your style settings
3237
* interface FramesStyleSettings {
@@ -66,18 +71,22 @@ export class PluginStyleSettings {
6671
readonly defaultValues: Partial<UnresolvedStyleSettings>
6772
readonly cssVarExclusions: StyleSettingPath[]
6873
readonly cssVarReplacements: [string, string][]
74+
readonly preventUnitlessValues: StyleSettingPath[]
6975

7076
constructor({
7177
defaultValues,
7278
cssVarExclusions = [],
7379
cssVarReplacements = [],
80+
preventUnitlessValues = [],
7481
}: {
7582
defaultValues: Partial<UnresolvedStyleSettings>
7683
cssVarExclusions?: StyleSettingPath[] | undefined
7784
cssVarReplacements?: [string, string][] | undefined
85+
preventUnitlessValues?: StyleSettingPath[] | undefined
7886
}) {
7987
this.defaultValues = defaultValues
8088
this.cssVarExclusions = cssVarExclusions
8189
this.cssVarReplacements = cssVarReplacements
90+
this.preventUnitlessValues = preventUnitlessValues
8291
}
8392
}

packages/@expressive-code/core/src/internal/core-styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ export const coreStyleSettings = new PluginStyleSettings({
214214
scrollbarThumbHoverColor: ({ theme, resolveSetting }) =>
215215
ensureColorContrastOnBackground(theme.colors['scrollbarSlider.hoverBackground'], resolveSetting('codeBackground'), 2.5, 3.5),
216216
} satisfies UnresolvedStyleSettings,
217+
preventUnitlessValues: ['borderRadius', 'borderWidth', 'gutterBorderWidth'],
217218
})
218219

219220
export function getCoreBaseStyles({

packages/@expressive-code/core/src/internal/style-resolving.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,32 @@ export function resolveStyleSettings({
3838
// Apply any theme style overrides
3939
applyStyleSettings(unresolvedByPath, getStyleSettingsByPath(theme.styleOverrides ?? {}))
4040

41+
// Collect all settings to be respected when resolving values
42+
const preventUnitlessValues = new Set<StyleSettingPath>(coreStyleSettings.preventUnitlessValues)
43+
plugins.forEach((plugin) => {
44+
plugin.styleSettings?.preventUnitlessValues.forEach((path) => preventUnitlessValues.add(path))
45+
})
46+
4147
// Define a setting resolver function that can be used both by plugins and ourselves
4248
function resolveSetting(settingPath: StyleSettingPath): string {
4349
let result = resolvedByPath.get(settingPath)
4450
if (result === undefined && !resolvedByPath.has(settingPath)) {
4551
if (attemptedToResolve.has(settingPath)) throw new Error(`Circular dependency detected while resolving style setting '${settingPath as string}'`)
4652
attemptedToResolve.add(settingPath)
4753

54+
// Resolve the setting value
4855
const valueOrResolver = unresolvedByPath.get(settingPath)
4956
const resolvedDefinition = (typeof valueOrResolver === 'function' ? valueOrResolver(resolverArgs) : valueOrResolver) as StyleValueOrValues
5057
result = Array.isArray(resolvedDefinition) ? resolvedDefinition[theme.type === 'dark' ? 0 : 1] : resolvedDefinition
5158

59+
// If the resolved result is required to have a unit,
60+
// ensure that it has one
61+
if (preventUnitlessValues.has(settingPath)) {
62+
result = result.trim()
63+
if (result in ['', 'none']) result = '0px'
64+
if (result.match(/^[0-9.]+$/)) result = `${result}px`
65+
}
66+
5267
resolvedByPath.set(settingPath, result)
5368
}
5469
if (result === undefined)

packages/@expressive-code/plugin-collapsible-sections/src/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export const collapsibleSectionsStyleSettings = new PluginStyleSettings({
108108
},
109109
},
110110
cssVarReplacements: [['collapsibleSections', 'cs']],
111+
preventUnitlessValues: ['collapsibleSections.closedBorderWidth', 'collapsibleSections.openBorderWidth'],
111112
})
112113

113114
export function getCollapsibleSectionsBaseStyles({ cssVar }: ResolverContext) {

packages/@expressive-code/plugin-frames/src/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export const framesStyleSettings = new PluginStyleSettings({
233233
tooltipSuccessForeground: 'white',
234234
},
235235
},
236+
preventUnitlessValues: ['frames.editorActiveTabIndicatorHeight', 'frames.editorTabBorderRadius'],
236237
})
237238

238239
export function getFramesBaseStyles({ cssVar }: ResolverContext, options: PluginFramesOptions) {

packages/@expressive-code/plugin-text-markers/src/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ export const textMarkersStyleSettings = new PluginStyleSettings({
234234
'textMarkers.indicatorLuminance',
235235
'textMarkers.indicatorOpacity',
236236
],
237+
preventUnitlessValues: ['textMarkers.lineMarkerLabelPaddingInline', 'textMarkers.lineMarkerAccentWidth'],
237238
})
238239

239240
export function getTextMarkersBaseStyles({ cssVar }: ResolverContext) {

0 commit comments

Comments
 (0)