with one click
style-set-create
새 컴포넌트의 StyleSet을 v2.0.0+ 컨벤션에 맞게 생성
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
새 컴포넌트의 StyleSet을 v2.0.0+ 컨벤션에 맞게 생성
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Style-Set 코드를 v2.0.0+ 컨벤션에 맞게 리뷰하고 개선 제안
Invoked manually via /write-readme. Generates README.md (English) and README.ko.md (Korean) from Vlossom source code. Supports all document types: components (vs-*), plugins, directives, composables (use*), and utility functions. Do NOT trigger automatically — only run when explicitly called with /write-readme <target>.
Use when vlossom feature/fix/removal work is done and commits are ready, to identify missing documentation files (tests, README, stories, markdown) and amend them back into the appropriate commits via interactive rebase with per-commit user confirmation.
Based on SOC occupation classification
| name | style-set-create |
| description | 새 컴포넌트의 StyleSet을 v2.0.0+ 컨벤션에 맞게 생성 |
새로운 컴포넌트의 StyleSet을 v2.0.0+ 컨벤션 (extends CSSProperties + $ prefix) 으로 설계하고 구현합니다.
VsXxxStyleSet은 CSSProperties를 상속하며, 키는 $ prefix 유무로 구분됩니다.
| 키 형태 | 의미 | 처리 |
|---|---|---|
width, padding, ... (non-$) | 표준 CSS — 컴포넌트 루트에 inline style로 적용 | componentInlineStyle로 자동 수집 |
$X (string/number) | CSS 변수로 노출 | --vs-{kebab-component}-X 로 emit (styleSetVariables) |
$X (object) | 슬롯/요소 스타일 또는 하위 StyleSet | componentStyleSet.$X로 직접 접근 |
import type { CSSProperties } from 'vue';
export interface VsButtonStyleSet extends CSSProperties {
$padding?: string; // CSS 변수: --vs-button-padding
$content?: CSSProperties; // 슬롯
$loading?: VsLoadingStyleSet; // 하위 컴포넌트
}
💡 root는
extends CSSProperties로 이미 노출되므로component?: CSSProperties같은 래퍼 키는 만들지 않습니다.
Vs[ComponentName]이 속성을 외부에서 자주 커스터마이징하나?
├── No → CSS에 하드코딩 (디자인 토큰 우선: var(--vs-comp-bg) 등)
└── Yes → 어떻게 노출할지 결정
├── 임의 CSS로 자유롭게 변경되어도 OK → root CSSProperties (extends 덕분에 자동)
├── .css에서 calc/의사 요소/상태 셀렉터에 쓰임 → $X primitive
├── 특정 슬롯/요소 스타일링 → $X: CSSProperties
└── 하위 Vlossom 컴포넌트 전체 스타일 전파 → $X: VsXxxStyleSet
$X primitive (CSS 변수) 사용 기준 — 4가지 모두 Yes일 때만.css에서 calc(), 의사 요소(::after), 상태 셀렉터에서 실제 사용?위 조건에 해당하지 않으면 root CSSProperties로 충분합니다.
width,height,padding,margin,boxShadow,backgroundColor등은 일반적으로 root에서 받는 것이 자연스럽습니다.
--vs-comp-bg, --vs-comp-font 등 기본 테마 색상은 ColorScheme이 자동 처리합니다. $backgroundColor, $fontColor로 별도 노출하지 마세요. 사용자가 일회성으로 바꿔야 한다면 root CSSProperties (backgroundColor) 로 직접 줄 수 있습니다.
import type { ComponentPublicInstance, CSSProperties } from 'vue';
import type Vs[ComponentName] from './Vs[ComponentName].vue';
// 하위 컴포넌트가 있다면 import
import type { VsChildStyleSet } from '@/components/vs-child/types';
declare module 'vue' {
interface GlobalComponents {
Vs[ComponentName]: typeof Vs[ComponentName];
}
}
export type { Vs[ComponentName] };
export interface Vs[ComponentName]Ref extends ComponentPublicInstance<typeof Vs[ComponentName]> {}
export interface Vs[ComponentName]StyleSet extends CSSProperties {
/** CSS 변수: --vs-[component-kebab]-arrowSize */
$arrowSize?: string;
/** 슬롯/요소 스타일 */
$element?: CSSProperties;
/** 하위 컴포넌트 StyleSet */
$childComponent?: VsChildStyleSet;
}
extends CSSProperties로 root CSS가 자동 노출되므로component?: CSSProperties같은 래퍼 키는 만들지 않습니다.
import { computed, defineComponent, ref, toRefs, type ComputedRef, type Ref } from 'vue';
import { VsComponent } from '@/declaration';
import { useColorScheme, useStyleSet } from '@/composables';
import { getColorSchemeProps, getStyleSetProps } from '@/props';
import { objectUtil } from '@/utils';
import type { Vs[ComponentName]StyleSet } from './types';
const componentName = VsComponent.Vs[ComponentName];
export default defineComponent({
name: componentName,
props: {
...getColorSchemeProps(),
...getStyleSetProps<Vs[ComponentName]StyleSet>(),
// 기타 props
},
setup(props) {
const { colorScheme, styleSet } = toRefs(props);
const { colorSchemeClass } = useColorScheme(componentName, colorScheme);
// 하위 컴포넌트 기본값 (고정값이면 ref 권장, 반응성 필요시 computed)
const baseStyleSet: Ref<Vs[ComponentName]StyleSet> = ref({
$childComponent: {
width: '100%', // 자식의 root CSSProperties
},
});
// props에서 동적 값이 오면 root에 직접 (extends CSSProperties 덕분에 평탄)
const additionalStyleSet = computed<Vs[ComponentName]StyleSet>(() => {
return objectUtil.shake({
height: props.height || undefined,
});
});
const { componentStyleSet, styleSetVariables, componentInlineStyle } =
useStyleSet<Vs[ComponentName]StyleSet>(
componentName,
styleSet,
baseStyleSet, // 3번째: 기본값 (가장 낮은 우선순위)
additionalStyleSet, // 4번째: props 동적 값 (가장 높은 우선순위, 사용자 styleSet도 덮음)
);
return {
colorSchemeClass,
styleSetVariables,
componentInlineStyle,
componentStyleSet,
};
},
});
useStyleSet 인자 규칙| 인자 | 용도 | 반응성 |
|---|---|---|
baseStyleSet (3번째) | 하위 컴포넌트 기본값, 고정 스타일 | ref({}) 권장 (고정값), computed 허용 (반응성 필요시) |
additionalStyleSet (4번째) | props에서 오는 동적 값 (사용자 styleSet보다 우선) | computed (항상) |
⚠️
additionalStyleSet은 사용자styleSet을 덮어쓰므로 신중히 사용하세요. props가 styleSet을 침범하지 않는 게 일반적입니다.
<template>
<div
:class="['vs-[component-kebab]', colorSchemeClass, classObj]"
:style="{ ...styleSetVariables, ...componentInlineStyle }"
>
<!-- 슬롯/요소 -->
<div
class="vs-[component-kebab]-element"
:style="componentStyleSet.$element"
>
<slot name="element" />
</div>
<!-- 하위 컴포넌트 -->
<vs-child-component :style-set="componentStyleSet.$childComponent" />
<slot />
</div>
</template>
핵심 포인트:
{ ...styleSetVariables, ...componentInlineStyle } 두 가지를 spreadcomponentStyleSet.$X (CSSProperties) → :stylecomponentStyleSet.$X (StyleSet) → :style-set$step?: CSSProperties & { $active?: CSSProperties };
computed로 base + 상태 스타일을 머지:
const stepStyle = computed<CSSProperties>(() => ({
...componentStyleSet.value.$step,
...(isActive.value ? componentStyleSet.value.$step?.$active : {}),
}));
.vs-[component-kebab] {
/* CSS 변수 선언 — 인터페이스의 $X primitive에 대응하며 .css에서 실제 사용하는 것만 */
--vs-[component-kebab]-arrowSize: initial;
/* 레이아웃과 기본 스타일은 직접 값 */
display: flex;
flex-direction: column;
/* CSS 변수 사용 (fallback 필수) */
padding-right: var(--vs-[component-kebab]-arrowSize, 1rem);
/* 디자인 토큰 우선 */
background-color: var(--vs-comp-bg);
color: var(--vs-comp-font);
border-radius: calc(var(--vs-radius-ratio) * var(--vs-radius-md));
}
.vs-[component-kebab]-element {
display: flex;
align-items: center;
gap: 0.5rem;
}
/* 상태별 스타일 */
.vs-[component-kebab]:hover {
opacity: 0.9;
}
.vs-[component-kebab].vs-disabled {
opacity: 0.5;
cursor: not-allowed;
}
규칙 요약:
--vs-{component-kebab}-{property} (property는 camelCase 유지)$X primitive ↔ CSS 변수가 1:1로 매칭되고 실제 사용되어야 함var(--vs-comp-bg) 등) 직접 사용Vs[ComponentName]StyleSet인가?extends CSSProperties로 선언되어 있는가?component?: CSSProperties 같은 래퍼 키가 없는가?variables?: { ... } 패턴이 남아있지 않은가?$X primitive는 4가지 기준을 모두 만족하는가?$backgroundColor, $fontColor)을 노출하지 않았는가? (ColorScheme 담당)$step.$active ✅)$content ✅, $expand ❌)$wrapper?: VsInputWrapperStyleSet가 포함되었는가?CSSProperties['objectFit'] & {})useStyleSet에서 componentStyleSet, styleSetVariables, componentInlineStyle 세 가지 모두 destructure했는가?:style에 { ...styleSetVariables, ...componentInlineStyle }을 spread했는가?baseStyleSet이 고정값이면 ref 사용을 검토했는가?componentStyleSet.$X를 :style로 바인딩했는가?componentStyleSet.$X를 :style-set으로 전달했는가?--vs-{component-kebab}-{property} 규칙을 따르는가?$X primitive와 CSS 변수가 1:1 매칭되는가?README.md의 Types 섹션이 새 인터페이스를 그대로 반영하는가?$X, root CSSProperties)을 따르는가?width, padding, boxShadow, backgroundColor → root CSSProperties (extends로 자동)$header, $body, $footer (object)$X primitive로 노출할 만한 속성 없음 (.css에서 calc/의사 요소가 없음)import type { ComponentPublicInstance, CSSProperties } from 'vue';
import type VsCard from './VsCard.vue';
declare module 'vue' {
interface GlobalComponents {
VsCard: typeof VsCard;
}
}
export type { VsCard };
export interface VsCardRef extends ComponentPublicInstance<typeof VsCard> {}
export interface VsCardStyleSet extends CSSProperties {
/** 헤더 영역 */
$header?: CSSProperties;
/** 바디 영역 */
$body?: CSSProperties;
/** 푸터 영역 */
$footer?: CSSProperties;
}
<vs-card
:style-set="{
width: '320px',
padding: '1rem',
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
$header: { fontWeight: 600 },
$footer: { borderTop: '1px solid #eee' },
}"
/>
component?: CSSProperties 같은 래퍼 키 추가 — extends CSSProperties로 이미 노출됨variables?: { ... } 구조 사용 — v2.0.0+ 컨벤션 아님$X (CSS 변수)로 — .css에서 실제 변수로 쓰이는 것만 노출$X: { $Y: { ... } }는 자식 StyleSet이 자체적으로 깊이를 가질 때만 허용$styles, $config, $inner 등 모호한 이름$backgroundColor, $fontColor 등$expand 대신 $content처럼 내용 기반으로root CSSProperties (extends로 자동):
$X primitive (CSS 변수):
.css에서 calc()나 의사 요소(::after)에서 참조해야 함$X object (CSSProperties):
$X (자식 StyleSet):
$loading, $chip, $wrapper)/style-set-create VsCard
대화형으로 요구사항을 파악하고 단계별로 생성합니다.