Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
122 lines
2.7 KiB
Markdown
122 lines
2.7 KiB
Markdown
# Use Computed Properties for Complex Class Logic
|
|
|
|
## Rule
|
|
|
|
When class bindings involve multiple conditions or complex logic, extract them into computed properties rather than writing inline expressions in templates.
|
|
|
|
## Why This Matters
|
|
|
|
- Inline class expressions quickly become unreadable with multiple conditions
|
|
- Computed properties are cached and only re-evaluate when dependencies change
|
|
- Logic in computed properties is easier to test and debug
|
|
- Keeps templates focused on structure, not logic
|
|
|
|
## Bad Code
|
|
|
|
```vue
|
|
<template>
|
|
<!-- Hard to read, error-prone -->
|
|
<div :class="{
|
|
'btn': true,
|
|
'btn-primary': type === 'primary' && !disabled,
|
|
'btn-secondary': type === 'secondary' && !disabled,
|
|
'btn-disabled': disabled,
|
|
'btn-loading': isLoading,
|
|
'btn-large': size === 'large',
|
|
'btn-small': size === 'small'
|
|
}">
|
|
{{ label }}
|
|
</div>
|
|
|
|
<!-- Even worse: string concatenation -->
|
|
<div :class="'btn btn-' + type + (disabled ? ' btn-disabled' : '') + (isLoading ? ' btn-loading' : '')">
|
|
{{ label }}
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## Good Code
|
|
|
|
```vue
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps({
|
|
type: { type: String, default: 'primary' },
|
|
size: { type: String, default: 'medium' },
|
|
disabled: Boolean,
|
|
isLoading: Boolean,
|
|
label: String
|
|
})
|
|
|
|
const buttonClasses = computed(() => ({
|
|
'btn': true,
|
|
[`btn-${props.type}`]: !props.disabled,
|
|
'btn-disabled': props.disabled,
|
|
'btn-loading': props.isLoading,
|
|
'btn-large': props.size === 'large',
|
|
'btn-small': props.size === 'small'
|
|
}))
|
|
</script>
|
|
|
|
<template>
|
|
<div :class="buttonClasses">
|
|
{{ label }}
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## Style Bindings Too
|
|
|
|
The same principle applies to style bindings:
|
|
|
|
```vue
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps({
|
|
color: String,
|
|
fontSize: Number,
|
|
isHighlighted: Boolean
|
|
})
|
|
|
|
const textStyles = computed(() => ({
|
|
color: props.color,
|
|
fontSize: `${props.fontSize}px`,
|
|
backgroundColor: props.isHighlighted ? 'yellow' : 'transparent',
|
|
fontWeight: props.isHighlighted ? 'bold' : 'normal'
|
|
}))
|
|
</script>
|
|
|
|
<template>
|
|
<span :style="textStyles">Styled text</span>
|
|
</template>
|
|
```
|
|
|
|
## Combining Static and Dynamic Classes
|
|
|
|
Use array syntax to combine static classes with computed dynamic classes:
|
|
|
|
```vue
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
const dynamicClasses = computed(() => ({
|
|
'is-active': isActive.value,
|
|
'is-disabled': isDisabled.value
|
|
}))
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Static 'card' class + dynamic classes -->
|
|
<div :class="['card', dynamicClasses]">
|
|
Content
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## References
|
|
|
|
- [Class and Style Bindings](https://vuejs.org/guide/essentials/class-and-style.html)
|
|
- [Computed Properties](https://vuejs.org/guide/essentials/computed.html)
|