변수를 사용한 Discourse 스타일링: 더 단순한 의미론의 필요성

Styling Discourse with variables: Show & Tell 토론을 이어갑니다:

Discourse의 테마 설정 경험을 개선하기 위한 노력이 정말 인상적입니다! 하지만 위 토픽에서 공유된 것처럼 많은 수의 CSS 변수를 추가하는 방식이 최적의 해결책인지에 대해서는 완전히 확신이 서지 않습니다. 그 이유에 대해 몇 가지 생각을 공유하고 싶습니다.

저는 Canvas Theme Template을 통해 이 접근 방식을 직접 실험해 왔습니다. 이 템플릿은 기본적으로 조정 가능한 변수들의 모음집을 제공하여 기본 테마를 구축할 수 있게 해줍니다:

:root {
  /* Layout */
  --d-max-width: 1110px;
  --canvas-nav-space: 0.75rem;
  --canvas-content-padding: 1.5rem;
  --canvas-topic-list-padding: 0.8em;
  
  /* Base Styles */
  --canvas-background: var(--secondary);
  --canvas-surface: var(--secondary);
  --canvas-border: 1px solid var(--primary-500);
  --canvas-border-light: 1px solid var(--primary-200);
  
  /* Border Radius */
  --d-border-radius: 2px;
  --d-border-radius-large: 2px;
  --d-button-border-radius: 2px;
  --d-input-border-radius: var(--d-button-border-radius);
  --d-nav-pill-border-radius: var(--d-button-border-radius);
  
  /* Button Styles */
  --canvas-button-padding: 0.5em 0.65em;
  --canvas-button-primary-padding: 0.5em 0.65em;
  
  /* Header */
  --canvas-header-height: 4rem;
  --canvas-header-background: var(--header_background);
  --canvas-header-border: none;
  --canvas-header-shadow: var(--shadow-header);
  
  /* Sidebar */
  --d-sidebar-width: 17em;
  --d-sidebar-background: var(--secondary);
  --canvas-sidebar-border: 1px solid var(--primary-low);
  --canvas-sidebar-scrollbar: var(--scrollbarWidth);
  --d-sidebar-row-height: 2.2em;
  --d-sidebar-highlight-background: var(--primary-low);

  /* And several more... */
}

이 방식은 간단한 조정에는 꽤 잘 작동하지만, 이 접근 방식을 확장하려고 할 때 몇 가지 한계에 부딪혔습니다:

인지적 부하 및 가시성(Discoverability)

광범위한 변수 목록은 사실상 조회 테이블(lookup table)이 필요합니다. 이는 컴포넌트 기반 프레임워크에서 일반적으로 컴포넌트를 스타일링하는 방식과는 동떨어져 느껴집니다. 그리고 이것은 저만의 생각일 수 있지만, 정신적 모델이 "이 컴포넌트를 스타일링하고 싶다"에서 "올바른 변수 이름을 찾아야 한다"로 전환되는 것 같습니다.

캐스케이딩 로직의 부재

현재 구현 방식은 적절한 캐스케이딩 계층 구조를 확립하지 않고 변수에 하드코딩된 값을 직접 할당합니다:

--d-sidebar-link-color: var(--primary-high);
--d-nav-background-color--active: transparent;
--table-border-width: 1px;

즉, --link-color--border-width와 같은 더 일반적인 변수로부터 상속이 이루어지지 않습니다. 체계적인 변경을 원할 경우, 하나의 근본적인 값을 변경하는 대신 여러 개의 구체적인 변수를 업데이트해야 합니다.

디자인-개발 간 괴리

이 방식은 디자인 도구(Figma 등)와 구현 사이에서 작업할 때 마찰을 유발한다고 생각합니다. 디자인 시스템은 일반적으로 이러한 매우 구현 특이적인 변수들과 1:1로 매핑되지 않는 의미론적 변수(semantic variables)를 사용합니다.

컴포넌트 아키텍처를 수용하는 대안적 접근법

기본적인 의미론적 변수와 신뢰할 수 있는 컴포넌트 타겟팅을 결합함으로써 동일한 목표를 더 자연스럽게 달성할 수 있다고 생각합니다. 긴 목록의 구체적인 변수 대신, Discourse 전반에서 유일하고 일관되게 명명된 컴포넌트 클래스에 의존할 수 있다면 어떨까요? .d-sidebar, .d-topic-list, .d-header 같은 것들이요.

그리고 실제로 CSS가 작동하도록 설계된 방식으로 캐스케이딩되는, 더 작은 규모의 기초 변수 세트와 이를 조합하는 것입니다:

/* Set the design foundation */
:root {
  --d-border-width: 2px;
  --d-surface-color: #3498db;
  --d-space-1: 1rem;
}

/* Override at the component level when you need to */
.d-topic-list,
.d-sidebar {
  --d-border-width: 1px;
}

제게는 이것이 CSS가 자연스럽게 작동하는 방식에 더 가깝게 느껴집니다. 전역 스타일을 설정한 다음 필요한 곳에서 세분화합니다. 앱 전체의 테두리 모양을 변경하고 싶으면 하나의 변수만 변경하면 됩니다. 사이드바를 다르게 만들고 싶으면 사이드바를 구체적으로 타겟팅하면 됩니다.

음, 나는 CSS가 자연스럽게 작동하는 방식이란 단순히 변수를 건너뛰고 이렇게 하는 것이라 생각한다.

/* 디자인 기반 설정 */
body {
  border-width: 2px;
  background-color: #3498db;
  margin: 1rem;
}

/* 필요할 때 컴포넌트 레벨에서 오버라이드 */
.d-topic-list,
.d-sidebar {
  border-width: 1px;
}
```\n
아마 내가 그냥 나이가 많은 탓일 수도 있다.

*진짜* 문제는 이거다.

[quote="manuel, post:1, topic:378001"]
Discourse 전체에서 고유하고 일관되게 이름 붙인 컴포넌트 클래스를 신뢰할 수 있다면 어떨까?
[/quote]

예를 들어, 단순히 `.btn` 또는 `button`을 타겟팅하는 경우:

.btn {
border: 1px solid red;
}


이것은 게시글 버튼들은 놓치지만 "views" 링크와 햄버거 메뉴는 타겟팅한다.

![image|690x235, 75%](upload://2mJopYlkjvTyInQ3RfLYD1Lbz2X.png)

변수를 사용해야 할 합당한 이유가 있으니, 여기서 그 부분을 자세히 논하진 않겠습니다. 다만 CSS의 본질에 대해 논쟁하지 않는다는 점은 좋은 지적입니다. 제가 더 명확하게 표현했어야 했는데, 이는 CSS의 본질에 관한 문제가 아니라 컴포넌트 기반 프레임워크를 스타일링할 때의 모범 사례에 관한 문제입니다. 그리고 버튼이 이러한 원칙을 적절히 적용하기 어려운 또 다른 좋은 예시라는 점에는 전적으로 동의합니다.

더 큰 그림을 보면, 프론트엔드 프레임워크의 JavaScript 측면을 현대화하기 위한 집중적인 노력이 있어 왔습니다. 그리고 저는 그것이 큰 성공을 거두었다고 생각합니다. 깨끗한 표준과 잘 구조화된 클래스를 다루는 것은 이제 정말로 즐거운 일이 되었습니다. 디자이너인 저에게도 이는 새로운 프론트엔드 컴포넌트를 더 쉽고 효율적으로 구축할 수 있는 기회를 열어주었습니다.

그러나 디자인 시스템을 동일한 표준으로 끌어올리는 데 비슷한 헌신이 없다는 느낌이 떠나지 않습니다. 모든 측면에 CSS 변수를 추가하는 것은 분명히 현재 방식보다 더 성능이 좋고 깔끔하지만, 여전히 더 깊은 수준의 아키텍처 문제를 회피하는 것처럼 느껴집니다: 지나치게 구체적인 선언으로 가득한 코드베이스와 명확한 컴포넌트 스코프 스타일의 부재 말입니다. 이는 더 어려운 문제, 즉 스타일링 아키텍처를 프레임워크의 모듈형 디자인과 완전히 일치시키는 문제를 피하는 “더 쉬운” 해결책처럼 느껴집니다.

이것이 많은 작업량과 하위 호환성 문제를 초래할 것임을 이해합니다. 하지만 팀은 JavaScript 측면에서 이러한 도전에 성공적으로 대응해 왔습니다. JavaScript가 스타일링보다 여전히 훨씬 더 많은 자원을 받게 된다면, 그 격차는 최종 디자인에서 드러날 것입니다. 그리고 사용자는 왜 그런지 설명할 수 없더라도 그 차이를 느끼게 될 것입니다.

저는 개발자 경험과 사용자 경험 모두에 장기적으로 혁신적인 혜택을 가져다줄 것이라고 확신하기 때문에, CSS 아키텍처에도 동일한 현대화 에너지가 적용되기를 진심으로 바랍니다.

우리가 취한 접근 방식은 당신이 설명하는 내용과 유사한 선상에 있는 것 같은 느낌이 들어, 어떻게 다르게 구현했을지 이해하기가 어렵습니다. 물론 피드백에는 항상 열려 있으며, 당신이 가져온 관점을 감사하게 생각합니다.

예를 들어, --space 같은 경우를 들어보면, 이것을 변경하면 앱 전체의 간격이 변경됩니다. 당신이 설명한 것과 유사한 접근 방식으로, 주제 목록이나 사이드바에만 영향을 주도록 대상화할 수도 있습니다.

일부 항목에 대해서는 맞지만, 모든 항목에 대해 그런 것은 아닙니다. 공유해 주실 다른 예시가 있다면 좋겠습니다!

이것은 분명히 문제입니다. 우리가 염두에 두고 있는 접근 방식(적어도 지금은 실험적으로)은 shadcn이 여기서 하고 있는 것과 유사한 편집기입니다:

완벽한 접근 방식은 아니지만, 인스펙터를 사용하는 방법이나 문서 메타데이터에 접근하거나 CSS를 사용하는 방법을 모르는 사람들에게 더 쉽게 사용할 수 있게 만드는 데 도움이 될 것 같습니다.

더 컴포넌트화된 접근 방식에 관해서는, 결국 도달하고 싶은 방향이지만, 현재 상태의 Discourse는 컴포넌트 기반 디자인을 염두에 두고 구축되지 않았습니다. 사용 가능한 변수를 추가하기 전에 그 단계에 도달하는 것을 기다리는 것은 옵션이 아니었습니다.

특정 섹션에서 구현을 더 쉽게 하기 위해 일부 클래스를 추가하는 것은 사용성 측면에서 좋은 전진 방향인 것 같습니다.


당신의 의견에 동의합니다 :+1:

짧은 답은 이렇게 하지 마세요 :sweat_smile:

btn-default, btn-primary, btn-flat와 같은 보조 버튼 클래스를 타겟팅하는 것이 훨씬 좋습니다. 이러한 보조 클래스는 버튼의 시각적 유형을 나타냅니다.

.btnbutton은 특정 외형보다는 더 넓은 의미의 "이것은 버튼이다"를 나타냅니다.

정말 정확합니다. 현재로서는 우리가 유지 관리하는 모든 테마와 함께 HTML과 CSS를 수년에 걸쳐 다시 작성하는 작업을 시작할 여유가 없습니다. 특히 Ember 업데이트라는 수년간의 여정을 아직 마무리하지 못한 점을 고려하면 더욱 그렇습니다.