|
| 1 | +--- |
| 2 | +outline: deep |
| 3 | +--- |
| 4 | + |
| 5 | +# CSS Objects and Style Functions |
| 6 | + |
| 7 | +Starting from version v1.12.0, `@vue-styled-components/core` supports CSS objects and style functions, providing more flexible ways to define component styles. |
| 8 | + |
| 9 | +## CSS Object Support |
| 10 | + |
| 11 | +You can now pass CSS objects directly to styled components instead of template literals. |
| 12 | + |
| 13 | +:::demo |
| 14 | + |
| 15 | +```vue |
| 16 | +<script setup lang="ts"> |
| 17 | +import { styled } from '@vue-styled-components/core' |
| 18 | +
|
| 19 | +const StyledDiv = styled.div({ |
| 20 | + color: 'red', |
| 21 | + fontSize: '16px', |
| 22 | + backgroundColor: 'blue', |
| 23 | + padding: '10px', |
| 24 | + borderRadius: '8px', |
| 25 | + textAlign: 'center' |
| 26 | +}) |
| 27 | +</script> |
| 28 | +
|
| 29 | +<template> |
| 30 | + <StyledDiv>CSS Object Styled Component</StyledDiv> |
| 31 | +</template> |
| 32 | +``` |
| 33 | + |
| 34 | +::: |
| 35 | + |
| 36 | +### Benefits |
| 37 | + |
| 38 | +- **Type Safety**: Better TypeScript support with autocomplete for CSS properties |
| 39 | +- **Dynamic Values**: Easier to work with computed values and variables |
| 40 | +- **Readability**: More structured and readable for complex styles |
| 41 | + |
| 42 | +## Style Functions |
| 43 | + |
| 44 | +Style functions allow you to create dynamic styles based on component props. |
| 45 | + |
| 46 | +:::demo |
| 47 | + |
| 48 | +```vue |
| 49 | +<script setup lang="ts"> |
| 50 | +import { styled } from '@vue-styled-components/core' |
| 51 | +import { ref } from 'vue' |
| 52 | +
|
| 53 | +const buttonProps = { |
| 54 | + disabled: Boolean, |
| 55 | + size: String |
| 56 | +} |
| 57 | +
|
| 58 | +const StyledButton = styled.button.props(buttonProps)(({ disabled, size }) => ({ |
| 59 | + color: disabled ? '#fff' : '#007bff', |
| 60 | + padding: size === 'large' ? '12px 24px' : size === 'small' ? '4px 8px' : '8px 16px', |
| 61 | + fontSize: size === 'large' ? '16px' : size === 'small' ? '12px' : '14px', |
| 62 | + border: 'none', |
| 63 | + borderRadius: '4px', |
| 64 | + backgroundColor: disabled ? '#ccc' : '#f8f9fa', |
| 65 | + cursor: disabled ? 'not-allowed' : 'pointer', |
| 66 | + transition: 'all 0.2s ease' |
| 67 | +})) |
| 68 | +
|
| 69 | +const disabled = ref(false) |
| 70 | +const size = ref<'small' | 'medium' | 'large'>('medium') |
| 71 | +</script> |
| 72 | +
|
| 73 | +<template> |
| 74 | + <div style="display: flex; flex-direction: column; gap: 16px; align-items: flex-start;"> |
| 75 | + <StyledButton :disabled="disabled" :size="size"> |
| 76 | + Dynamic Button ({{ size }}) |
| 77 | + </StyledButton> |
| 78 | + |
| 79 | + <div style="display: flex; gap: 8px;"> |
| 80 | + <button @click="size = 'small'">Small</button> |
| 81 | + <button @click="size = 'medium'">Medium</button> |
| 82 | + <button @click="size = 'large'">Large</button> |
| 83 | + <button @click="disabled = !disabled"> |
| 84 | + {{ disabled ? 'Enable' : 'Disable' }} |
| 85 | + </button> |
| 86 | + </div> |
| 87 | + </div> |
| 88 | +</template> |
| 89 | +``` |
| 90 | + |
| 91 | +::: |
| 92 | + |
| 93 | +## Props Chain Calling |
| 94 | + |
| 95 | +You can use the `.props()` method to define component props separately, then chain with element methods. |
| 96 | + |
| 97 | +:::demo |
| 98 | + |
| 99 | +```vue |
| 100 | +<script setup lang="ts"> |
| 101 | +import { styled } from '@vue-styled-components/core' |
| 102 | +import { ref } from 'vue' |
| 103 | +
|
| 104 | +const StyledInput = styled |
| 105 | + .input |
| 106 | + .props({ |
| 107 | + borderColor: { type: String, default: '#ccc' }, |
| 108 | + size: { type: String, default: 'medium' } |
| 109 | + })(({ borderColor, size }) => ({ |
| 110 | + border: `1px solid ${borderColor}`, |
| 111 | + padding: size === 'large' ? '12px' : size === 'small' ? '4px' : '8px', |
| 112 | + fontSize: size === 'large' ? '16px' : size === 'small' ? '12px' : '14px', |
| 113 | + borderRadius: '4px', |
| 114 | + outline: 'none', |
| 115 | + transition: 'border-color 0.2s ease', |
| 116 | + width: '100%' |
| 117 | + })) |
| 118 | +
|
| 119 | +const borderColor = ref('#ccc') |
| 120 | +const size = ref('medium') |
| 121 | +</script> |
| 122 | +
|
| 123 | +<template> |
| 124 | + <div style="display: flex; flex-direction: column; gap: 16px;"> |
| 125 | + <StyledInput |
| 126 | + :borderColor="borderColor" |
| 127 | + :size="size" |
| 128 | + placeholder="Type something..." |
| 129 | + @focus="borderColor = '#007bff'" |
| 130 | + @blur="borderColor = '#ccc'" |
| 131 | + /> |
| 132 | + |
| 133 | + <div style="display: flex; gap: 8px;"> |
| 134 | + <button @click="size = 'small'">Small</button> |
| 135 | + <button @click="size = 'medium'">Medium</button> |
| 136 | + <button @click="size = 'large'">Large</button> |
| 137 | + </div> |
| 138 | + </div> |
| 139 | +</template> |
| 140 | +``` |
| 141 | + |
| 142 | +::: |
| 143 | + |
| 144 | +## Combined Usage |
| 145 | + |
| 146 | +You can combine props definition with style functions for more complex components. |
| 147 | + |
| 148 | +:::demo |
| 149 | + |
| 150 | +```vue |
| 151 | +<script setup lang="ts"> |
| 152 | +import { styled } from '@vue-styled-components/core' |
| 153 | +import { ref } from 'vue' |
| 154 | +
|
| 155 | +const StyledCard = styled |
| 156 | + .div |
| 157 | + .props({ |
| 158 | + elevation: { type: Number, default: 1 }, |
| 159 | + rounded: { type: Boolean, default: true }, |
| 160 | + variant: { type: String, default: 'default' } |
| 161 | + })(({ elevation, rounded, variant }) => ({ |
| 162 | + boxShadow: `0 ${elevation * 2}px ${elevation * 4}px rgba(0,0,0,0.1)`, |
| 163 | + borderRadius: rounded ? '8px' : '0', |
| 164 | + padding: '16px', |
| 165 | + backgroundColor: variant === 'primary' ? '#007bff' : variant === 'success' ? '#28a745' : 'white', |
| 166 | + color: variant === 'default' ? '#333' : 'white', |
| 167 | + border: variant === 'default' ? '1px solid #e9ecef' : 'none', |
| 168 | + transition: 'all 0.2s ease', |
| 169 | + cursor: 'pointer' |
| 170 | + })) |
| 171 | +
|
| 172 | +const elevation = ref(1) |
| 173 | +const rounded = ref(true) |
| 174 | +const variant = ref('default') |
| 175 | +</script> |
| 176 | +
|
| 177 | +<template> |
| 178 | + <div style="display: flex; flex-direction: column; gap: 16px;"> |
| 179 | + <StyledCard |
| 180 | + :elevation="elevation" |
| 181 | + :rounded="rounded" |
| 182 | + :variant="variant" |
| 183 | + > |
| 184 | + This is a {{ variant }} card, shadow level {{ elevation }}, rounded: {{ rounded }} |
| 185 | + </StyledCard> |
| 186 | + |
| 187 | + <div style="display: flex; flex-wrap: wrap; gap: 8px;"> |
| 188 | + <div> |
| 189 | + <label>阴影级别: </label> |
| 190 | + <input |
| 191 | + type="range" |
| 192 | + min="0" |
| 193 | + max="5" |
| 194 | + v-model.number="elevation" |
| 195 | + /> |
| 196 | + {{ elevation }} |
| 197 | + </div> |
| 198 | + |
| 199 | + <label> |
| 200 | + <input type="checkbox" v-model="rounded" /> |
| 201 | + 圆角 |
| 202 | + </label> |
| 203 | + |
| 204 | + <select v-model="variant"> |
| 205 | + <option value="default">默认</option> |
| 206 | + <option value="primary">主要</option> |
| 207 | + <option value="success">成功</option> |
| 208 | + </select> |
| 209 | + </div> |
| 210 | + </div> |
| 211 | +</template> |
| 212 | +``` |
| 213 | + |
| 214 | +::: |
| 215 | + |
| 216 | +## Key Features |
| 217 | + |
| 218 | +1. **CSS Object Conversion**: Automatically converts camelCase CSS properties to kebab-case |
| 219 | +2. **Style Functions**: Support functions that receive props and return CSS objects |
| 220 | +3. **Props Chain Calling**: Define component props through `.props()` method, then chain with element methods |
| 221 | +4. **Type Safety**: Full TypeScript support ensuring type safety for props and styles |
| 222 | +5. **Backward Compatibility**: Maintains full compatibility with existing template string syntax |
0 commit comments