이 글에서 다루는 내용

Quasar에서 flat이라는 prop을 쓰면 SCSS 셀렉터 .q-btn--flat이 매칭되는 이유가 궁금했다. prop은 Vue의 영역이고 SCSS는 CSS의 영역인데, 둘이 어떻게 연결되는 걸까? 이 글에서는 그 연결 고리를 BEM 방법론과 함께 정리한다.


발단 — 이 SCSS 코드에서 시작됐다

.app-btn.q-btn,
.app-ghost-btn.q-btn.q-btn--flat {
  &.h-92 {
    height: 92px;
    font-size: 30px;
  }
}

여기서 .q-btn--flat이라는 셀렉터가 눈에 띄었다. flat은 Quasar의 prop인데, 왜 SCSS에서 클래스처럼 사용되는 걸까?


prop이 클래스로 변환된다

핵심은 Quasar가 flat prop을 받으면 내부 JS 로직이 DOM에 클래스를 자동으로 추가한다는 것이다.

<!-- 개발자가 작성한 코드 -->
<q-btn class="app-ghost-btn" flat />

<!-- 실제 렌더링된 DOM -->
<button class="app-ghost-btn q-btn q-btn--flat ...">

흐름을 정리하면:

flat prop (Vue)
  → Quasar 내부 JS가 감지
  → q-btn--flat 클래스를 DOM에 추가
  → SCSS 셀렉터가 매칭

즉, SCSS가 flat prop 자체를 선택하는 게 아니라, Quasar가 그 prop을 보고 붙여준 클래스를 선택하는 것이다.


BEM 방법론과의 관계

q-btn--flat에서 --는 BEM 방법론의 Modifier를 의미한다.

q-btn           ← Block
q-btn--flat     ← Block + Modifier (상태/변형)
q-btn__label    ← Block + Element
  • -- : 변형/상태 (flat, round, disabled 등)
  • __ : 구성 요소 (label, icon, wrapper 등)

Quasar가 BEM 네이밍을 따르기 때문에 prop 값에 따라 q-btn--flat, q-btn--round, q-btn--disabled 같은 클래스를 자동으로 붙여주는 것이다.


__--의 차이

__ Element -- Modifier
생성 시점 렌더링 시 항상 고정 prop/상태에 따라 동적
용도 내부 구조 표현 변형/상태 표현
q-btn__label q-btn--flat

__는 prop 없이 컴포넌트 내부 HTML 구조 자체가 항상 그 클래스를 가진다. 반면 --는 prop 값에 따라 조건부로 붙는다.

<!-- q-btn 렌더링 결과 -->
<button class="q-btn">
  <span class="q-btn__wrapper">    <!-- 항상 존재 -->
    <span class="q-btn__content">  <!-- 항상 존재 -->
      <span class="q-btn__label">  <!-- 항상 존재 -->
        Click
      </span>
    </span>
  </span>
</button>

클래스를 직접 써도 될까?

<q-btn class="q-btn--flat" />

스타일은 적용되지만 Quasar 내부 동작(접근성, 기타 로직)은 작동하지 않을 수 있다. prop을 통해 쓰는 것이 올바른 방법이다.


이건 Quasar만의 패턴이 아니다

대부분의 UI 프레임워크가 동일한 방식으로 동작한다.

<!-- Vuetify -->
<v-btn outlined />  →  v-btn--outlined

<!-- MUI (React) -->
<Button variant="outlined" />  →  MuiButton-outlined

<!-- Angular Material -->
<mat-button color="primary" />  →  mat-button-primary

네이티브도 마찬가지다.

// iOS SwiftUI
Button("Click").buttonStyle(.bordered)
// Android Jetpack Compose
Button(colors = ButtonDefaults.outlinedButtonColors())

공통 철학:

개발자는 "어떻게 보일지"를 prop/속성으로 선언
              ↓
프레임워크가 내부적으로 스타일(클래스)로 변환

정리

개념 설명
flat prop Vue/Quasar의 영역. 의미 있는 값으로 선언
q-btn--flat BEM Modifier 클래스. DOM에 동적으로 추가됨
SCSS 셀렉터 DOM에 추가된 클래스를 선택
BEM __ 내부 구조, 항상 고정
BEM -- 상태/변형, 조건부

눈에 보이는 코드(Vue prop, SCSS)가 분리되어 있고 그 사이를 프레임워크 JS가 연결하기 때문에 처음엔 암묵적으로 느껴질 수 있다. 브라우저 DevTools에서 실제 DOM 클래스를 확인하면서 연결 고리를 직접 눈으로 보는 것이 가장 빠른 이해 방법이다.