Skip to content

Commit

Permalink
feat(core): add option to output the internal layers as CSS Cascade L…
Browse files Browse the repository at this point in the history
…ayers (#3587)

Co-authored-by: Simon He <57086651+Simon-He95@users.noreply.github.com>
  • Loading branch information
RebeccaStevens and Simon-He95 committed Mar 14, 2024
1 parent cc09252 commit b798da0
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 0 deletions.
14 changes: 14 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ Raw CSS injections.

Layer orders. Default to 0.

### outputToCssLayers
- **Type:** `boolean | UseCssLayersOptions`
- **Default:** `false`

Outputs the layers to CSS Cascade Layers.

#### cssLayerName
- **Type:** `(internalLayer: string) => string | undefined | null`

Specifies the name of the CSS layer the internal layer should be output to (can be a sublayer e.g. "mylayer.mysublayer").

If `undefined` is return, the internal layer name wil be used as the CSS layer name.
If `null` is return, the internal layer will not be output to a CSS layer.

### sortLayers
- **Type:** `(layers: string[]) => string[]`

Expand Down
24 changes: 24 additions & 0 deletions docs/config/layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,27 @@ import './my-custom.css'
// "utilities" layer will have the highest priority
import 'uno:utilities.css'
```

## CSS Cascade Layers

You can output CSS Cascade Layers by:

```ts
outputToCssLayers: true
```

You can change the CSS Layer names with:

```ts
outputToCssLayers: (layer) => {
// The default layer will be output to the "utilities" CSS layer.
if (layer === 'default')
return 'utilities'

// The shortcuts layer will be output to the "shortcuts" sublayer the of "utilities" CSS layer.
if (layer === 'shortcuts')
return 'utilities.shortcuts'

// All other layer will just use their name as the CSS layer name.
}
```
15 changes: 15 additions & 0 deletions packages/core/src/generator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ export class UnoGenerator<Theme extends object = object> {
extendedInfo = false,
} = options

const outputCssLayers = this.config.outputToCssLayers

const tokens: Readonly<Set<string> | CountableSet<string>> = isString(input)
? await this.applyExtractors(
input,
Expand Down Expand Up @@ -337,6 +339,19 @@ export class UnoGenerator<Theme extends object = object> {
.join(nl)
}

if (outputCssLayers && css) {
let cssLayer = typeof outputCssLayers === 'object'
? (outputCssLayers.cssLayerName?.(layer))
: undefined

if (cssLayer !== null) {
if (!cssLayer)
cssLayer = layer

css = `@layer ${cssLayer}{${nl}${css}${nl}}`
}
}

const layerMark = minify ? '' : `/* layer: ${layer} */${nl}`
return layerCache[layer] = css ? layerMark + css : ''
}
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@ export interface ConfigBase<Theme extends object = object> {
*/
layers?: Record<string, number>

/**
* Output the internal layers as CSS Cascade Layers.
*/
outputToCssLayers?: boolean | OutputCssLayersOptions

/**
* Custom function to sort layers.
*/
Expand Down Expand Up @@ -464,6 +469,16 @@ export interface ConfigBase<Theme extends object = object> {
details?: boolean
}

export interface OutputCssLayersOptions {

/**
* Specify the css layer that the internal layer should be output to.
*
* Return `null` to specify that the layer should not be output to any css layer.
*/
cssLayerName?: (internalLayer: string) => string | undefined | null
}

export type AutoCompleteTemplate = string
export type AutoCompleteFunction = (input: string) => Awaitable<string[]>

Expand Down
57 changes: 57 additions & 0 deletions test/__snapshots__/use-css-layer.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`output-to-css-layer > static 1`] = `
"/* layer: shortcuts */
@layer shortcuts{
.abc{name:bar1;name:bar2;name:4;}
}
/* layer: a */
@layer a{
.a{name:bar1;}
}
/* layer: b */
@layer b{
.b{name:bar2;}
}
/* layer: c */
@layer c{
.c5{name:5;}
}
/* layer: d */
@layer d{
/* RAW 4 */
/* RAW 3 */
}
/* layer: s */
@layer s{
.abcd{name:bar1;name:bar2;name:2;}
}"
`;

exports[`use-css-layer > static 1`] = `
"/* layer: shortcuts */
@layer shortcuts{
.abc{name:bar1;name:bar2;name:4;}
}
/* layer: a */
@layer a{
.a{name:bar1;}
}
/* layer: b */
@layer b{
.b{name:bar2;}
}
/* layer: c */
@layer c{
.c5{name:5;}
}
/* layer: d */
@layer d{
/* RAW 4 */
/* RAW 3 */
}
/* layer: s */
@layer s{
.abcd{name:bar1;name:bar2;name:2;}
}"
`;
23 changes: 23 additions & 0 deletions test/use-css-layer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createGenerator } from '@unocss/core'
import { describe, expect, it } from 'vitest'

describe('use-css-layer', () => {
it('static', async () => {
const uno = createGenerator({
rules: [
['a', { name: 'bar1' }, { layer: 'a' }],
['b', { name: 'bar2' }, { layer: 'b' }],
[/^c(\d+)$/, ([, d]) => ({ name: d }), { layer: 'c' }],
[/^d(\d+)$/, ([, d]) => `/* RAW ${d} */`, { layer: 'd' }],
],
shortcuts: [
['abcd', 'a b c2 d3', { layer: 's' }],
['abc', 'a b c4'],
],
presets: [],
outputToCssLayers: true,
})
const { css } = await uno.generate('a b abc abcd d4 c5', { preflights: false })
expect(css).toMatchSnapshot()
})
})

0 comments on commit b798da0

Please sign in to comment.