Skip to content

Commit 213c2d8

Browse files
authored
Merge pull request #20 from v-vibe/feat/css-compiler
feat: auto-add vendor prefixes to CSS styles and add support for nested CSS in legacy browsers
2 parents ce5eef7 + b261868 commit 213c2d8

16 files changed

+187
-38
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747
✅ Add or override attrs
4848

49-
✅ Support nesting css. (web only: https://drafts.csswg.org/css-nesting/#nesting)
49+
✅ Support nesting css.
5050

5151
## 📖Documentation
5252

@@ -180,4 +180,7 @@ const StyledDiv = styled.div`
180180

181181
<br>
182182

183-
And thanks [styled-components](https://github.com/styled-components).
183+
And thanks
184+
185+
- [styled-components](https://github.com/styled-components).
186+
- [stylis](https://github.com/thysultan/stylis)

commitlint.config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
export default {
22
extends: ['@commitlint/config-conventional'],
3+
rules: {
4+
'header-max-length': [0, 'always', 125],
5+
},
36
}

core/helper/createGlobalStyle.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import type { ExpressionType } from '@/utils'
22
import { defineComponent, DefineSetupFnComponent, h } from 'vue'
3-
import { generateComponentName, insertExpressions } from '@/utils'
3+
import { generateComponentName, generateUniqueName, insertExpressions } from '@/utils'
44
import { injectStyle } from '@/utils'
55

66
export const createGlobalStyle = (styles: TemplateStringsArray, ...expressions: ExpressionType[]): DefineSetupFnComponent<any> => {
77
return defineComponent(
88
(_, { attrs }) => {
9+
const golbalClassName = `global-${generateUniqueName()}`
910
const cssStringsWithExpression = insertExpressions(styles, expressions)
10-
injectStyle('global', cssStringsWithExpression, attrs)
11+
injectStyle(golbalClassName, cssStringsWithExpression, attrs)
1112
return () => {
1213
return h('div', { style: 'display: none' })
1314
}

core/styled.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import { type ExpressionType, generateClassName, generateComponentName, insertEx
1616
import { isStyledComponent, isValidElementType, isVueComponent } from '@/helper'
1717

1818
interface IProps {
19-
as?: SupportedHTMLElements
19+
as?: PropType<SupportedHTMLElements>
2020
}
2121

2222
type ComponentCustomProps = PublicProps & {
2323
styled: boolean
2424
}
2525

26-
export type StyledComponentType = DefineSetupFnComponent<IProps, any, SlotsType, any, ComponentCustomProps>
26+
export type StyledComponentType<P = any> = DefineSetupFnComponent<IProps & P, any, SlotsType, IProps & P, ComponentCustomProps>
2727

2828
type StyledFactory = <T = Record<string, any>>(
2929
styles: TemplateStringsArray,
@@ -34,7 +34,7 @@ type StyledComponent = StyledFactory & {
3434
}
3535
type Attrs = Record<string, any>
3636

37-
function baseStyled(target: string | InstanceType<any>, propsDefinition: Record<string, unknown> = {}): StyledComponent {
37+
function baseStyled<P extends Record<string, any>>(target: string | InstanceType<any>, propsDefinition?: P & IProps): StyledComponent {
3838
if (!isValidElementType(target)) {
3939
throw Error('The element is invalid.')
4040
}

core/utils/styleManagement.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { applyExpressions, ExpressionType } from '@/utils/index'
2+
import { compile, middleware, prefixer, serialize, stringify } from 'stylis'
23

34
const MAX_SIZE = 65536
45

@@ -24,11 +25,7 @@ function insert(className: string, cssString: string) {
2425
}
2526

2627
const ruleNode = insertedRuleMap[className]
27-
let rule = `.${className} { ${cssString} }`
28-
29-
if (className === 'global' || /^kf-.+/.test(className)) {
30-
rule = cssString
31-
}
28+
const rule = cssString
3229

3330
if (ruleNode) {
3431
ruleNode.data = rule
@@ -42,7 +39,7 @@ function insert(className: string, cssString: string) {
4239
export function removeStyle(className: string): void {
4340
for (const tag of tags) {
4441
for (const node of tag.childNodes) {
45-
if (node.nodeValue?.includes(className)) {
42+
if (node.nodeValue?.startsWith(`.${className}`)) {
4643
node.remove()
4744
break
4845
}
@@ -52,5 +49,6 @@ export function removeStyle(className: string): void {
5249

5350
export function injectStyle<T>(className: string, cssWithExpression: ExpressionType<T>[], context: Record<string, any>): void {
5451
const appliedCss = applyExpressions(cssWithExpression, context).join('')
55-
insert(className, appliedCss)
52+
const compiledCss = serialize(compile(`.${className}{${appliedCss}}`), middleware([prefixer, stringify]))
53+
insert(className, compiledCss)
5654
}

docs/.vitepress/en.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ function sidebarGuide() {
5454
items: [
5555
{ text: 'Theming', link: 'theming' },
5656
{ text: 'Global Styles', link: 'global-style' },
57-
{ text: 'CSS Mixin', link: 'css-mixin' }
57+
{ text: 'CSS Mixin', link: 'css-mixin' },
58+
{ text: 'Nest CSS', link: 'nest-css' },
59+
{ text: 'Auto Prefix', link: 'auto-prefix' },
5860
],
5961
},
6062
{

docs/.vitepress/zh.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ function sidebarGuide() {
7878
items: [
7979
{ text: '主题', link: 'theming' },
8080
{ text: '全局样式', link: 'global-style' },
81-
{ text: '样式复用', link: 'css-mixin' }
81+
{ text: '样式复用', link: 'css-mixin' },
82+
{ text: '嵌套CSS', link: 'nest-css' },
83+
{ text: 'CSS私有前缀', link: 'auto-prefix' },
8284
],
8385
},
8486
{

docs/guide/advances/auto-prefix.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# Auto Prefix Added
6+
7+
This feature is a part of the `@vvibe/vue-styled-components` package. It automatically adds vendor prefixes to CSS rules based on the browser support you specify. This ensures that your CSS rules are compatible with the most widely used browsers, which can help improve the compatibility of your website.
8+
9+
The `@vvibe/vue-styled-components` package uses the `stylis` library to add vendor prefixes.
10+
11+
Here's an example:
12+
13+
```js
14+
import { styled } from '@vvibe/vue-styled-components';
15+
const StyledDiv = styled.div`
16+
display: flex;
17+
}`
18+
19+
// output:
20+
// .styled-div {
21+
// display: -webkit-box;
22+
// display: -webkit-flex;
23+
// display: -ms-flexbox;
24+
// display: flex;
25+
// }
26+
```

docs/guide/advances/nest-css.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# Nest CSS
6+
7+
In Vue Styled Components, you can write CSS in the same way as `less` or `sass`. `@vvibe/vue-styled-components` is based on `stylis` compiling CSS to basic css, so you needn't worry about the compatibility.
8+
9+
For example, you can write CSS like this:
10+
11+
```js
12+
import { styled } from '@vvibe/vue-styled-components';
13+
const StyledDiv = styled.div`
14+
width: 100px;
15+
height: 100px;
16+
&:hover {
17+
background-color: #000;
18+
&:active {
19+
background-color: #fff;
20+
}
21+
}
22+
}`
23+
24+
// output:
25+
// .styled-xxx {
26+
// width: 100px;
27+
// height: 100px;
28+
// }
29+
// .styled-xxx:hover {
30+
// background-color: #000;
31+
// }
32+
// .styled-xxx:hover:active {
33+
// background-color: #fff;
34+
// }
35+
```

docs/zh/guide/advances/auto-prefix.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# 自动添加浏览器私有前缀
6+
7+
The `@vvibe/vue-styled-components` package uses the `stylis` library to add vendor prefixes.
8+
9+
这是 `@vvibe/vue-styled-components` 包的一部分。它会自动为 CSS 规则添加浏览器私有前缀。这可以确保你的 CSS 规则在最常见的浏览器中兼容,这有助于提高你的网站兼容性。
10+
11+
`@vvibe/vue-styled-components` 使用 `stylis` 库来编译以及添加浏览器私有前缀。
12+
13+
```js
14+
import { styled } from '@vvibe/vue-styled-components';
15+
const StyledDiv = styled.div`
16+
display: flex;
17+
}`
18+
19+
// output:
20+
// .styled-div {
21+
// display: -webkit-box;
22+
// display: -webkit-flex;
23+
// display: -ms-flexbox;
24+
// display: flex;
25+
// }
26+
```

docs/zh/guide/advances/nest-css.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# 嵌套 CSS
6+
7+
你可以像 `less`, `sass` 一样使用嵌套 CSS。 `@vvibe/vue-styled-components` 并非使用最新的CSS规范([nest css](https://drafts.csswg.org/css-nesting/#nesting))来实现,而是使用了 `stylis` 编译为最基础的 CSS,因此你不需要担心其兼容性问题。
8+
9+
如下:
10+
11+
```js
12+
import { styled } from '@vvibe/vue-styled-components';
13+
const StyledDiv = styled.div`
14+
width: 100px;
15+
height: 100px;
16+
background-color: #ccc;
17+
color: #000;
18+
&:hover {
19+
background-color: #000;
20+
color: #fff;
21+
&:active {
22+
background-color: #fff;
23+
}
24+
}
25+
}`
26+
27+
// output:
28+
// .styled-xxx {
29+
// width: 100px;
30+
// height: 100px;
31+
// }
32+
// .styled-xxx:hover {
33+
// background-color: #000;
34+
// }
35+
// .styled-xxx:hover:active {
36+
// background-color: #fff;
37+
// }
38+
```

example/App.vue

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { styled, ThemeProvider, keyframes, withAttrs, css, cssClass } from '@vvide/vue-styled-components'
2+
import { styled, ThemeProvider, keyframes, withAttrs, css, cssClass } from '@vvibe/vue-styled-components'
33
import Component from './Component.vue'
44
import { ref } from 'vue'
55
@@ -25,6 +25,7 @@ const update = () => {
2525
}
2626
2727
const StyledComp3 = styled(Component)`
28+
position: sticky;
2829
background: ${(props) => props.theme.primary};
2930
`
3031
const StyledComp4 = styled.div`
@@ -118,10 +119,12 @@ const TestEmbedComponent = styled('div', { show: Boolean })`
118119
`
119120
120121
// console.log(testEmbedCss1, testEmbedCss2)
122+
const visible = ref(true)
121123
</script>
122124

123125
<template>
124126
<ThemeProvider :theme="theme">
127+
<div @click="visible = !visible">Test Remove</div>
125128
<StyledComp3 @click="update">12345</StyledComp3>
126129
<StyledComp4>12345</StyledComp4>
127130
<StyledComp5>12345</StyledComp5>
@@ -133,6 +136,10 @@ const TestEmbedComponent = styled('div', { show: Boolean })`
133136

134137
<TestEmbedComponent :show="show"> White </TestEmbedComponent>
135138
<TestEmbedComponent :show="!show" @click="show = !show"> Blue </TestEmbedComponent>
139+
140+
<IconInner color="red" size="55"> 666 </IconInner>
141+
142+
<BlueButton v-if="visible" />
136143
</ThemeProvider>
137144
</template>
138145

package.json

+4-5
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@
3535
"prepare": "husky",
3636
"release": "semantic-release"
3737
},
38-
"dependencies": {
38+
"peerDependencies": {
3939
"vue": "^3.4.21"
4040
},
4141
"devDependencies": {
4242
"@commitlint/cli": "^19.2.1",
4343
"@commitlint/config-conventional": "^19.1.0",
4444
"@semantic-release/changelog": "^6.0.3",
4545
"@semantic-release/git": "^10.0.1",
46+
"@types/stylis": "^4.2.6",
4647
"@typescript-eslint/eslint-plugin": "^7.4.0",
4748
"@typescript-eslint/parser": "^7.4.0",
4849
"@vitejs/plugin-vue": "^5.0.4",
@@ -70,9 +71,7 @@
7071
"access": "public",
7172
"provenance": true
7273
},
73-
"pnpm": {
74-
"overrides": {
75-
"vue-template-compiler@>=2.0.0 <3.0.0": ">=3.0.0"
76-
}
74+
"dependencies": {
75+
"stylis": "^4.3.2"
7776
}
7877
}

0 commit comments

Comments
 (0)