choiminyeong 5e5ef8c36f 플랫폼 최초 커밋 + 자동 업데이트 배포 폴더(dist)
소스(Core/Shell/Launcher)와 실행 PC가 받아갈 업데이트 파일(dist)을 함께 올린다.
Launcher가 켤 때 이 저장소의 dist와 D:\UbisamPlatform\bin을 비교해 새 버전이면 받아간다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 21:56:35 +09:00

61 KiB
Raw Blame History

다크모드 색상 + 입력 컨트롤 디자인 — 통합 지시서

이 문서 전체를 Claude Code에 붙여넣고 "이 지시서대로 적용해줘"라고 요청하세요. 패치 04 ~ 08을 하나로 합친 것입니다 — 이것만 적용하면 됩니다.


당신은 UbisamBase WPF 솔루션(.NET Framework 4.8, MVVM + CommunityToolkit.Mvvm)에서 작업합니다. 아래 §3의 파일은 전문이 주어져 있으니 그대로 덮어쓰거나 새로 만드세요. 값을 임의로 재해석하지 마세요.

1. 다크모드 색상 규칙

검정 바탕이 아니라 차가운 회색 3단 층입니다. 층이 밝아지는 순서가 구조를 만듭니다.

층 값 무엇
바탕 #23262B 컨텐츠 영역
판 #2A2E34 상단바 · 하단 탭 바 · 서브탭 바
입력 #2F343A TextBox · ComboBox · 표 헤더
경계 #3A3F47 (셸) / #454B54 (입력) 헤어라인 1px
본문 글자 #E8EAED · 보조 #C2C7CE · 흐린 #8A9099

accent는 두 단계로 씁니다. 이 구분이 핵심입니다.

역할 다크 라이트 쓰는 곳
Ubisam.Brush.Accent #4DA6F5 #2196F3 선 · 글자 · 탭 밑줄 · 포커스 테두리 — 바탕 위에 얹히는 것
Ubisam.Brush.Accent.Fill #1E7FD4 #1D82E8 글자를 얹는 채움면 — 주 버튼, 로고 타일, 선택된 항목, 체크박스
Ubisam.Brush.Accent.OnFill #F2F7FC #FFFFFF 그 채움면 위의 글자 · 체크 표시

밝은 단계(#4DA6F5)에 검은 글자도, 흰 글자도 얹지 마세요 — 대비가 부족합니다. 글자가 올라가는 면은 항상 깊은 단계(#1E7FD4, 흰 글자와 약 4.2:1)를 씁니다.

2. 컨트롤 디자인 규칙

셸과 같은 도면 톤입니다.

  • 각진 모서리 — 라운드 없음. 알약형 세그먼트, 둥근 버튼 금지
  • 1px 헤어라인 경계 — 두꺼운 테두리나 그림자로 층을 만들지 않음
  • 포커스 = accent 테두리 + 옅은 글로우(BlurRadius 6, 55%). 브라우저/OS 기본 포커스 링 사용 금지
  • 비활성 = 45% 불투명도 또는 어두운 배경 + 흐린 글자
  • 높이 34 통일 (TextBox · ComboBox · Button)
  • 상태 열은 태그로 — 판정·레벨 같은 열을 맨 텍스트로 두면 예외 행이 안 보입니다
  • 수치 열은 우측 정렬 + 자릿수 고정(tabular-nums)

컨트롤별로:

컨트롤 디자인
TextBox 어두운 입력면, 포커스 시 accent 테두리 + 글로우, 읽기 전용은 어두운 배경
ComboBox 각진 닫힘 상태 + 우측 화살표(열리면 뒤집힘 + accent), 드롭다운은 입력창 바로 아래에 붙고 위 테두리 없음, 선택 항목은 Accent.Fill + 흰 글자
CheckBox 17×17 각진 박스, 체크 시 Accent.Fill + 흰 체크
Button 암시적 = 보조(외곽선). 주 동작은 Ubisam.Style.Button.Primary(Accent.Fill 채움), 세 번째는 .Ghost
ListView + GridView 읽기 전용 표. 헤더 회색 판 + 세로 구분선, 행 hover/선택 accent 틴트
DataGrid 편집 가능한 격자. 정렬 열은 accent 글자 + 화살표, 행 머리(26px)가 선택 시 accent 바, 편집 중인 셀만 accent 테두리, 짝수 행 옅은 줄무늬
Slider 얇은 홈 트랙 + 지나온 구간만 accent, 썸은 각진 10×20 세로 막대(둥근 다이얼 아님)
ScrollBar 폭 10, 트랙 비움, 썸만 회색 → hover 시 accent, 화살표 버튼 없음

전부 암시적 스타일(x:Key 없음)이라 각 View를 고칠 필요가 없습니다. 예외를 두려면 그 컨트롤에 Style="{x:Null}"을 주세요.

3. 파일 전문

src/UbisamBase.Core/Themes/Colors.Dark.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <!-- 다크: 검정이 아니라 차가운 회색 바탕 + 회사 하늘색.
         판(#2A2E34)이 바탕(#23262B)보다 한 단계 밝아 층이 구분된다.
         accent는 두 단계로 쓴다 — 선·글자·밑줄은 밝은 #4DA6F5, 글자를 얹는 채움면은
         깊은 #1E7FD4 + 흰 글자(#F2F7FC). 밝은 단계에 검은 글자를 얹지 않는다. -->
    <SolidColorBrush x:Key="Ubisam.Brush.ContentBackground" Color="#FF23262B"/>
    <SolidColorBrush x:Key="Ubisam.Brush.ContentForeground" Color="#FFE8EAED"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Accent" Color="#FF4DA6F5"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Accent.Fill" Color="#FF1E7FD4"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Accent.OnFill" Color="#FFF2F7FC"/>

    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.Background" Color="#FF2A2E34"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.Foreground" Color="#FFC2C7CE"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.HoverBackground" Color="#16FFFFFF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.SelectedBackground" Color="#1A4DA6F5"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.SelectedForeground" Color="#FF4DA6F5"/>

    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.Background" Color="#FF2A2E34"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.Border" Color="#FF3A3F47"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.Foreground" Color="#FFC2C7CE"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.HoverBackground" Color="#16FFFFFF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.SelectedBackground" Color="#FF1E7FD4"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.SelectedForeground" Color="#FFF2F7FC"/>

    <!-- 입력 컨트롤 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Input.Background" Color="#FF2F343A"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.Border" Color="#FF454B54"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.BorderHover" Color="#FF5A616B"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.DisabledBackground" Color="#FF292D32"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.DisabledBorder" Color="#FF383D44"/>
    <Color x:Key="Ubisam.Color.AccentGlow">#FF4DA6F5</Color>

    <!-- 표 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Table.HeaderBackground" Color="#FF2F343A"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Table.RowBorder" Color="#FF383D44"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Table.GridLine" Color="#FF383D44"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Table.AltRowBackground" Color="#FF333840"/>

    <!-- 상태 태그 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.AccentBackground" Color="#FF123A5C"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.AccentForeground" Color="#FFA8D4FA"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.NeutralBackground" Color="#FF383D44"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.NeutralForeground" Color="#FFC2C7CE"/>

    <SolidColorBrush x:Key="Ubisam.Brush.Slot.Hatch" Color="#1A4DA6F5"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Muted" Color="#FF8A9099"/>

</ResourceDictionary>

src/UbisamBase.Core/Themes/Colors.Light.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <!-- 라이트: 기술 도면 톤의 밝은 바탕 + 회사 하늘색 accent -->
    <SolidColorBrush x:Key="Ubisam.Brush.ContentBackground" Color="#FFF2F2F3"/>
    <SolidColorBrush x:Key="Ubisam.Brush.ContentForeground" Color="#FF1D1F20"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Accent" Color="#FF2196F3"/>
    <!-- 글자를 얹는 채움면용 깊은 단계 + 그 위 글자색 (다크와 키를 공유) -->
    <SolidColorBrush x:Key="Ubisam.Brush.Accent.Fill" Color="#FF1D82E8"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Accent.OnFill" Color="#FFFFFFFF"/>

    <!-- 상단 브랜딩 바 = 흰 판, 하단 탭 바와 같은 계열 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.Background" Color="#FFFFFFFF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.Foreground" Color="#FF5D5D60"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.HoverBackground" Color="#14000000"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.SelectedBackground" Color="#122196F3"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Sidebar.SelectedForeground" Color="#FF2196F3"/>

    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.Background" Color="#FFFFFFFF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.Border" Color="#FFD6D6D9"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.Foreground" Color="#FF5D5D60"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.HoverBackground" Color="#14000000"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.SelectedBackground" Color="#FF2196F3"/>
    <SolidColorBrush x:Key="Ubisam.Brush.SubTabBar.SelectedForeground" Color="#FFFFFFFF"/>

    <!-- 입력 컨트롤 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Input.Background" Color="#FFFFFFFF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.Border" Color="#FFC9C9CC"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.BorderHover" Color="#FFA8A8AC"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.DisabledBackground" Color="#FFEDEDEF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Input.DisabledBorder" Color="#FFDEDEE1"/>
    <Color x:Key="Ubisam.Color.AccentGlow">#FF2196F3</Color>

    <!-- 표 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Table.HeaderBackground" Color="#FFEDEDEF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Table.RowBorder" Color="#FFEDEDEF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Table.GridLine" Color="#FFDEDEE1"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Table.AltRowBackground" Color="#FFF8F8F9"/>

    <!-- 상태 태그 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.AccentBackground" Color="#FFDCEBFA"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.AccentForeground" Color="#FF0B4C7E"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.NeutralBackground" Color="#FFEDEDEF"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Tag.NeutralForeground" Color="#FF3F3F42"/>

    <!-- 빈 뷰 슬롯의 사선 해치 색 -->
    <SolidColorBrush x:Key="Ubisam.Brush.Slot.Hatch" Color="#172196F3"/>
    <SolidColorBrush x:Key="Ubisam.Brush.Muted" Color="#FF98989B"/>

</ResourceDictionary>

src/UbisamBase.Core/Themes/Controls.xaml

<!-- 입력 컨트롤 · 표 스타일. 전부 암시적 스타일(x:Key 없음)이라
     각 View를 고치지 않아도 셸 안의 모든 TextBox/ComboBox/CheckBox/Button/ListBox/ListView에 적용된다.
     특정 위치만 예외로 두고 싶으면 그 컨트롤에 Style="{x:Null}"을 주면 된다. -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <!-- ══ TextBox ══ -->
    <Style TargetType="TextBox">
        <Setter Property="MinHeight" Value="34"/>
        <Setter Property="Padding" Value="10,0"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="13.5"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Setter Property="CaretBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
        <Setter Property="SelectionBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TextBox">
                    <Border x:Name="Bd"
                            Background="{DynamicResource Ubisam.Brush.Input.Background}"
                            BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}"
                            BorderThickness="1"
                            SnapsToDevicePixels="True">
                        <ScrollViewer x:Name="PART_ContentHost"
                                      Margin="{TemplateBinding Padding}"
                                      VerticalAlignment="Center"
                                      Focusable="False"
                                      HorizontalScrollBarVisibility="Hidden"
                                      VerticalScrollBarVisibility="Hidden"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.BorderHover}"/>
                        </Trigger>
                        <Trigger Property="IsKeyboardFocusWithin" Value="True">
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                            <Setter TargetName="Bd" Property="Effect">
                                <Setter.Value>
                                    <DropShadowEffect Color="{DynamicResource Ubisam.Color.AccentGlow}"
                                                      BlurRadius="6" ShadowDepth="0" Opacity="0.55"/>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsEnabled" Value="False"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Input.DisabledBackground}"/>
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.DisabledBorder}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Muted}"/>
                        </MultiTrigger>
                        <Trigger Property="IsReadOnly" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Input.DisabledBackground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- ══ ComboBox ══ -->
    <Style x:Key="Ubisam.Style.ComboToggle" TargetType="ToggleButton">
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="ClickMode" Value="Press"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ToggleButton">
                    <Border x:Name="Bd"
                            Background="{DynamicResource Ubisam.Brush.Input.Background}"
                            BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}"
                            BorderThickness="1"
                            SnapsToDevicePixels="True">
                        <Path x:Name="Arrow"
                              Data="M 0,0 L 5,5 L 10,0"
                              Stroke="{DynamicResource Ubisam.Brush.Sidebar.Foreground}"
                              StrokeThickness="1.5"
                              StrokeStartLineCap="Round"
                              StrokeEndLineCap="Round"
                              StrokeLineJoin="Round"
                              HorizontalAlignment="Right"
                              VerticalAlignment="Center"
                              Margin="0,0,11,0"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.BorderHover}"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                            <Setter TargetName="Arrow" Property="Stroke" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                            <Setter TargetName="Arrow" Property="RenderTransformOrigin" Value="0.5,0.5"/>
                            <Setter TargetName="Arrow" Property="RenderTransform">
                                <Setter.Value>
                                    <ScaleTransform ScaleY="-1"/>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Input.DisabledBackground}"/>
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.DisabledBorder}"/>
                            <Setter TargetName="Arrow" Property="Stroke" Value="{DynamicResource Ubisam.Brush.Muted}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="ComboBoxItem">
        <Setter Property="Padding" Value="11,8"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="13.5"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ComboBoxItem">
                    <Border x:Name="Bd" Background="Transparent" Padding="{TemplateBinding Padding}">
                        <ContentPresenter VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedBackground}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
                        </Trigger>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Accent.Fill}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Accent.OnFill}"/>
                            <Setter Property="FontWeight" Value="SemiBold"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="ComboBox">
        <Setter Property="MinHeight" Value="34"/>
        <Setter Property="Padding" Value="10,0,30,0"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="13.5"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ComboBox">
                    <Grid>
                        <ToggleButton x:Name="ToggleButton"
                                      Style="{StaticResource Ubisam.Style.ComboToggle}"
                                      IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <ContentPresenter x:Name="ContentSite"
                                          Content="{TemplateBinding SelectionBoxItem}"
                                          ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                                          ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
                                          Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="Center"
                                          HorizontalAlignment="Left"
                                          IsHitTestVisible="False"/>
                        <TextBox x:Name="PART_EditableTextBox"
                                 Style="{x:Null}"
                                 Visibility="Collapsed"
                                 Margin="{TemplateBinding Padding}"
                                 Background="Transparent"
                                 BorderThickness="0"/>
                        <Popup x:Name="Popup"
                               Placement="Bottom"
                               IsOpen="{TemplateBinding IsDropDownOpen}"
                               AllowsTransparency="True"
                               Focusable="False"
                               PopupAnimation="Fade">
                            <Border MinWidth="{TemplateBinding ActualWidth}"
                                    MaxHeight="{TemplateBinding MaxDropDownHeight}"
                                    Background="{DynamicResource Ubisam.Brush.Input.Background}"
                                    BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}"
                                    BorderThickness="1,0,1,1"
                                    Effect="{DynamicResource Ubisam.Effect.Popup}">
                                <ScrollViewer>
                                    <ItemsPresenter/>
                                </ScrollViewer>
                            </Border>
                        </Popup>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEditable" Value="True">
                            <Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
                            <Setter TargetName="ContentSite" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Muted}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- ══ CheckBox ══ -->
    <Style TargetType="CheckBox">
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="13.5"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="CheckBox">
                    <StackPanel Orientation="Horizontal">
                        <Border x:Name="Box"
                                Width="17" Height="17"
                                Background="{DynamicResource Ubisam.Brush.Input.Background}"
                                BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}"
                                BorderThickness="1"
                                SnapsToDevicePixels="True"
                                VerticalAlignment="Center">
                            <Path x:Name="Check"
                                  Data="M 1,6 L 5,10 L 12,2"
                                  Stroke="{DynamicResource Ubisam.Brush.Accent.OnFill}"
                                  StrokeThickness="2"
                                  StrokeStartLineCap="Round"
                                  StrokeEndLineCap="Round"
                                  StrokeLineJoin="Round"
                                  Visibility="Collapsed"
                                  HorizontalAlignment="Center"
                                  VerticalAlignment="Center"/>
                        </Border>
                        <ContentPresenter Margin="9,0,0,0" VerticalAlignment="Center"/>
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Box" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.BorderHover}"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter TargetName="Box" Property="Background" Value="{DynamicResource Ubisam.Brush.Accent.Fill}"/>
                            <Setter TargetName="Box" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Accent.Fill}"/>
                            <Setter TargetName="Check" Property="Visibility" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Box" Property="Background" Value="{DynamicResource Ubisam.Brush.Input.DisabledBackground}"/>
                            <Setter TargetName="Box" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.DisabledBorder}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Muted}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- ══ Button ══
         암시적 = 보조(외곽선). 주 동작에는 Style="{StaticResource Ubisam.Style.Button.Primary}" -->
    <Style TargetType="Button">
        <Setter Property="MinHeight" Value="34"/>
        <Setter Property="Padding" Value="20,8"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="13.5"/>
        <Setter Property="FontWeight" Value="Medium"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.Foreground}"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="Bd"
                            Background="{DynamicResource Ubisam.Brush.Input.Background}"
                            BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}"
                            BorderThickness="1"
                            Padding="{TemplateBinding Padding}"
                            SnapsToDevicePixels="True">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedBackground}"/>
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.HoverBackground}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Bd" Property="Opacity" Value="0.45"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="Ubisam.Style.Button.Primary" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Accent.OnFill}"/>
        <Setter Property="FontWeight" Value="SemiBold"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="Bd"
                            Background="{DynamicResource Ubisam.Brush.Accent.Fill}"
                            BorderThickness="0"
                            Padding="{TemplateBinding Padding}"
                            SnapsToDevicePixels="True">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Opacity" Value="0.88"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="Bd" Property="Opacity" Value="0.75"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Bd" Property="Opacity" Value="0.45"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="Ubisam.Style.Button.Ghost" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="Bd" Background="Transparent" Padding="{TemplateBinding Padding}">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.HoverBackground}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- ══ 일반 ListBox (설정 키 목록 등) ══ -->
    <Style TargetType="ListBoxItem">
        <Setter Property="Padding" Value="11,8"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="13.5"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <Border x:Name="Bd"
                            Background="Transparent"
                            BorderThickness="2,0,0,0"
                            BorderBrush="Transparent"
                            Padding="{TemplateBinding Padding}">
                        <ContentPresenter VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.HoverBackground}"/>
                        </Trigger>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedBackground}"/>
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
                            <Setter Property="FontWeight" Value="SemiBold"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="ListBox">
        <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Input.Background}"/>
        <Setter Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.Border}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="0,4"/>
    </Style>

    <!-- ══ ListView + GridView (로그 표) ══ -->
    <Style TargetType="GridViewColumnHeader">
        <Setter Property="Padding" Value="11,9"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="11"/>
        <Setter Property="FontWeight" Value="SemiBold"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.Foreground}"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="GridViewColumnHeader">
                    <Border x:Name="Bd"
                            Background="{DynamicResource Ubisam.Brush.Table.HeaderBackground}"
                            BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}"
                            BorderThickness="0,0,1,1"
                            Padding="{TemplateBinding Padding}">
                        <ContentPresenter VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.HoverBackground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="Ubisam.Style.TableRow" TargetType="ListViewItem">
        <Setter Property="Padding" Value="0"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="12.5"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListViewItem">
                    <Border x:Name="Bd"
                            Background="Transparent"
                            BorderBrush="{DynamicResource Ubisam.Brush.Table.RowBorder}"
                            BorderThickness="0,0,0,1"
                            Padding="0,4">
                        <GridViewRowPresenter Content="{TemplateBinding Content}"
                                              Columns="{TemplateBinding GridView.ColumnCollection}"
                                              VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.HoverBackground}"/>
                        </Trigger>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedBackground}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="ListView">
        <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Input.Background}"/>
        <Setter Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.Border}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="ItemContainerStyle" Value="{StaticResource Ubisam.Style.TableRow}"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    </Style>

    <!-- ══ DataGrid ══
         ListView+GridView가 읽기 전용 표라면 DataGrid는 편집 가능한 격자다.
         셀 구분선은 세로만 그리고(가로는 행 경계가 담당), 선택은 행 머리의 accent 바 + 행 틴트로 알린다. -->

    <!-- 정렬 화살표 -->
    <Geometry x:Key="Ubisam.Geometry.SortAsc">M1,6 L5.5,1 L10,6</Geometry>
    <Geometry x:Key="Ubisam.Geometry.SortDesc">M1,1 L5.5,6 L10,1</Geometry>

    <Style TargetType="DataGridColumnHeader">
        <Setter Property="Padding" Value="11,10"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="11"/>
        <Setter Property="FontWeight" Value="SemiBold"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.Foreground}"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="DataGridColumnHeader">
                    <Grid>
                        <Border x:Name="Bd"
                                Background="{DynamicResource Ubisam.Brush.Table.HeaderBackground}"
                                BorderBrush="{DynamicResource Ubisam.Brush.Table.GridLine}"
                                BorderThickness="0,0,1,0"
                                Padding="{TemplateBinding Padding}">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <ContentPresenter Grid.Column="0"
                                                  VerticalAlignment="Center"
                                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                                <Path x:Name="SortArrow"
                                      Grid.Column="1"
                                      Margin="7,0,0,0"
                                      Data="{StaticResource Ubisam.Geometry.SortDesc}"
                                      Stroke="{DynamicResource Ubisam.Brush.Accent}"
                                      StrokeThickness="2"
                                      StrokeStartLineCap="Round"
                                      StrokeEndLineCap="Round"
                                      StrokeLineJoin="Round"
                                      VerticalAlignment="Center"
                                      Visibility="Collapsed"/>
                            </Grid>
                        </Border>
                        <Thumb x:Name="PART_RightHeaderGripper"
                               HorizontalAlignment="Right"
                               Width="6"
                               Cursor="SizeWE"
                               Opacity="0">
                            <Thumb.Template>
                                <ControlTemplate TargetType="Thumb">
                                    <Border Background="Transparent"/>
                                </ControlTemplate>
                            </Thumb.Template>
                        </Thumb>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.HoverBackground}"/>
                        </Trigger>
                        <Trigger Property="SortDirection" Value="Ascending">
                            <Setter TargetName="SortArrow" Property="Visibility" Value="Visible"/>
                            <Setter TargetName="SortArrow" Property="Data" Value="{StaticResource Ubisam.Geometry.SortAsc}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
                        </Trigger>
                        <Trigger Property="SortDirection" Value="Descending">
                            <Setter TargetName="SortArrow" Property="Visibility" Value="Visible"/>
                            <Setter TargetName="SortArrow" Property="Data" Value="{StaticResource Ubisam.Geometry.SortDesc}"/>
                            <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- 행 머리: 폭 26, 선택 시 accent 바가 채워진다 -->
    <Style TargetType="DataGridRowHeader">
        <Setter Property="Width" Value="26"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="DataGridRowHeader">
                    <Border x:Name="Bd"
                            Background="Transparent"
                            BorderBrush="{DynamicResource Ubisam.Brush.Table.GridLine}"
                            BorderThickness="0,0,1,0"/>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow}}" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="DataGridCell">
        <Setter Property="Padding" Value="11,9"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="DataGridCell">
                    <Border x:Name="Bd"
                            Background="Transparent"
                            BorderBrush="{DynamicResource Ubisam.Brush.Table.RowBorder}"
                            BorderThickness="0,0,1,0"
                            Padding="{TemplateBinding Padding}">
                        <ContentPresenter VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <!-- 편집 중인 셀만 accent 테두리 + 내부 여백을 줄여 입력칸을 보여준다 -->
                        <Trigger Property="IsEditing" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Input.Background}"/>
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                            <Setter TargetName="Bd" Property="BorderThickness" Value="1"/>
                            <Setter TargetName="Bd" Property="Padding" Value="6,4"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="DataGridRow">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="12.5"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.ContentForeground}"/>
        <Style.Triggers>
            <Trigger Property="AlternationIndex" Value="1">
                <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Table.AltRowBackground}"/>
            </Trigger>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.HoverBackground}"/>
            </Trigger>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedBackground}"/>
                <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Sidebar.SelectedForeground}"/>
            </Trigger>
        </Style.Triggers>
    </Style>

    <Style TargetType="DataGrid">
        <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Input.Background}"/>
        <Setter Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Input.Border}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="RowBackground" Value="Transparent"/>
        <Setter Property="AlternatingRowBackground" Value="{DynamicResource Ubisam.Brush.Table.AltRowBackground}"/>
        <Setter Property="RowHeight" Value="34"/>
        <Setter Property="ColumnHeaderHeight" Value="34"/>
        <Setter Property="GridLinesVisibility" Value="Horizontal"/>
        <Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource Ubisam.Brush.Table.RowBorder}"/>
        <Setter Property="VerticalGridLinesBrush" Value="{DynamicResource Ubisam.Brush.Table.RowBorder}"/>
        <Setter Property="HeadersVisibility" Value="All"/>
        <Setter Property="SelectionUnit" Value="FullRow"/>
        <Setter Property="GridLinesVisibility" Value="All"/>
        <Setter Property="AutoGenerateColumns" Value="False"/>
        <Setter Property="CanUserAddRows" Value="False"/>
        <Setter Property="CanUserDeleteRows" Value="False"/>
        <Setter Property="CanUserResizeRows" Value="False"/>
        <Setter Property="EnableRowVirtualization" Value="True"/>
    </Style>

    <!-- 수치 열에 붙일 셀 스타일: 우측 정렬 + 자릿수 고정 -->
    <Style x:Key="Ubisam.Style.NumericCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="HorizontalContentAlignment" Value="Right"/>
        <Setter Property="TextBlock.TextAlignment" Value="Right"/>
    </Style>

    <!-- ══ 상태 태그 ══
         표의 판정·레벨 같은 상태 열은 맨 텍스트가 아니라 태그로 그린다 (디자인 시스템 규칙).
         DataGridTemplateColumn / GridViewColumn의 CellTemplate 안에서 쓴다. -->
    <Style x:Key="Ubisam.Style.Tag" TargetType="Border">
        <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Tag.NeutralBackground}"/>
        <Setter Property="CornerRadius" Value="3"/>
        <Setter Property="Padding" Value="10,3"/>
        <Setter Property="HorizontalAlignment" Value="Left"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
    </Style>

    <Style x:Key="Ubisam.Style.TagText" TargetType="TextBlock">
        <Setter Property="FontFamily" Value="{DynamicResource Ubisam.Font.Body}"/>
        <Setter Property="FontSize" Value="11"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Tag.NeutralForeground}"/>
    </Style>

    <!-- 정상·완료: accent 옅은 채움 -->
    <Style x:Key="Ubisam.Style.Tag.Accent" TargetType="Border" BasedOn="{StaticResource Ubisam.Style.Tag}">
        <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.Tag.AccentBackground}"/>
    </Style>
    <Style x:Key="Ubisam.Style.TagText.Accent" TargetType="TextBlock" BasedOn="{StaticResource Ubisam.Style.TagText}">
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Tag.AccentForeground}"/>
    </Style>

    <!-- 주의·재검: 외곽선만 (예외 행이 한눈에 걸리도록) -->
    <Style x:Key="Ubisam.Style.Tag.Outline" TargetType="Border" BasedOn="{StaticResource Ubisam.Style.Tag}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="{DynamicResource Ubisam.Brush.Accent}"/>
        <Setter Property="BorderThickness" Value="1"/>
    </Style>
    <Style x:Key="Ubisam.Style.TagText.Outline" TargetType="TextBlock" BasedOn="{StaticResource Ubisam.Style.TagText}">
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Accent}"/>
    </Style>

    <!-- ══ Slider ══
         트랙은 얇은 홈, 채워진 구간만 accent. 썸은 각진 세로 막대(다이얼이 아니라 계기 슬라이드). -->
    <Style x:Key="Ubisam.Style.SliderThumb" TargetType="Thumb">
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Thumb">
                    <Border x:Name="Bd"
                            Width="10" Height="20"
                            Background="{DynamicResource Ubisam.Brush.Accent.Fill}"
                            BorderBrush="{DynamicResource Ubisam.Brush.Accent}"
                            BorderThickness="1"
                            SnapsToDevicePixels="True"/>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                        </Trigger>
                        <Trigger Property="IsDragging" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="Slider">
        <Setter Property="MinHeight" Value="24"/>
        <Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Accent}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Slider">
                    <Grid VerticalAlignment="Center">
                        <!-- 트랙 홈 -->
                        <Border Height="4"
                                Background="{DynamicResource Ubisam.Brush.Input.DisabledBackground}"
                                BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}"
                                BorderThickness="1"
                                VerticalAlignment="Center"
                                SnapsToDevicePixels="True"/>
                        <Track x:Name="PART_Track">
                            <Track.DecreaseRepeatButton>
                                <RepeatButton Command="Slider.DecreaseLarge"
                                              Focusable="False"
                                              Height="4"
                                              VerticalAlignment="Center">
                                    <RepeatButton.Template>
                                        <ControlTemplate TargetType="RepeatButton">
                                            <!-- 채워진 구간 -->
                                            <Border Background="{DynamicResource Ubisam.Brush.Accent}"
                                                    Height="4"
                                                    VerticalAlignment="Center"
                                                    SnapsToDevicePixels="True"/>
                                        </ControlTemplate>
                                    </RepeatButton.Template>
                                </RepeatButton>
                            </Track.DecreaseRepeatButton>
                            <Track.IncreaseRepeatButton>
                                <RepeatButton Command="Slider.IncreaseLarge" Focusable="False">
                                    <RepeatButton.Template>
                                        <ControlTemplate TargetType="RepeatButton">
                                            <Border Background="Transparent"/>
                                        </ControlTemplate>
                                    </RepeatButton.Template>
                                </RepeatButton>
                            </Track.IncreaseRepeatButton>
                            <Track.Thumb>
                                <Thumb Style="{StaticResource Ubisam.Style.SliderThumb}"/>
                            </Track.Thumb>
                        </Track>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Opacity" Value="0.45"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- ══ ScrollBar ══
         폭 10의 얇은 막대. 트랙은 비우고 썸만 회색 — hover에서 accent로. 화살표 버튼은 없앤다. -->
    <Style x:Key="Ubisam.Style.ScrollThumb" TargetType="Thumb">
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Thumb">
                    <Border x:Name="Bd"
                            Background="{DynamicResource Ubisam.Brush.Input.BorderHover}"
                            SnapsToDevicePixels="True"/>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                        </Trigger>
                        <Trigger Property="IsDragging" Value="True">
                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource Ubisam.Brush.Accent}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="ScrollBar">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Width" Value="10"/>
        <Setter Property="MinWidth" Value="10"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ScrollBar">
                    <Grid Background="{TemplateBinding Background}">
                        <Track x:Name="PART_Track" IsDirectionReversed="True">
                            <Track.DecreaseRepeatButton>
                                <RepeatButton Command="ScrollBar.PageUpCommand" Focusable="False">
                                    <RepeatButton.Template>
                                        <ControlTemplate TargetType="RepeatButton">
                                            <Border Background="Transparent"/>
                                        </ControlTemplate>
                                    </RepeatButton.Template>
                                </RepeatButton>
                            </Track.DecreaseRepeatButton>
                            <Track.IncreaseRepeatButton>
                                <RepeatButton Command="ScrollBar.PageDownCommand" Focusable="False">
                                    <RepeatButton.Template>
                                        <ControlTemplate TargetType="RepeatButton">
                                            <Border Background="Transparent"/>
                                        </ControlTemplate>
                                    </RepeatButton.Template>
                                </RepeatButton>
                            </Track.IncreaseRepeatButton>
                            <Track.Thumb>
                                <Thumb Style="{StaticResource Ubisam.Style.ScrollThumb}" Margin="3,0"/>
                            </Track.Thumb>
                        </Track>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Orientation" Value="Horizontal">
                            <Setter Property="Height" Value="10"/>
                            <Setter Property="MinHeight" Value="10"/>
                            <Setter Property="Width" Value="Auto"/>
                            <Setter Property="MinWidth" Value="0"/>
                            <Setter TargetName="PART_Track" Property="IsDirectionReversed" Value="False"/>
                            <Setter TargetName="PART_Track" Property="Orientation" Value="Horizontal"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

4. src/UbisamBase.Core/Themes/ShellStyles.xaml — 머지 추가

Controls.xaml을 머지 목록에 넣고, ComboBox 드롭다운 그림자를 정의하세요.

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="pack://application:,,,/UbisamBase.Core;component/Themes/Icons.xaml"/>
    <ResourceDictionary Source="pack://application:,,,/UbisamBase.Core;component/Themes/Controls.xaml"/>
</ResourceDictionary.MergedDictionaries>
<DropShadowEffect x:Key="Ubisam.Effect.Popup" BlurRadius="12" ShadowDepth="3" Direction="270" Opacity="0.22" Color="#000000"/>

중요 — 암시적 스타일이 안 걸릴 때: Controls.xaml을 ShellWindow.Resources에만 머지하면, 리소스 조회 범위 밖의 UserControl에는 적용되지 않습니다. 콤보박스가 여전히 WPF 기본 크롬(둥근 모서리 + 밝은 회색)으로 보이면 App.xaml의 Application.Resources에 같은 머지를 넣으세요.

5. src/UbisamBase.Core/Shell/ShellWindow.xaml — 로고 타일

로고 타일은 글자를 얹는 채움면이므로 밝은 단계가 아니라 채움 단계를 씁니다.

<Border Width="30" Height="30" Background="{DynamicResource Ubisam.Brush.Accent.Fill}">
    <TextBlock Text="{Binding Brand, Converter={StaticResource FirstCharConverter}}"
               Foreground="{DynamicResource Ubisam.Brush.Accent.OnFill}"
               FontFamily="{StaticResource Ubisam.Font.Heading}"
               FontWeight="SemiBold" FontSize="17"
               HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>

반대로 ShellStyles.xaml의 좌측 네비 선택 테두리와 하단 탭 인디케이터 바는 글자를 얹지 않는 선이므로 Accent(밝은 단계)가 맞습니다 — 그대로 두세요.

6. 상태 태그 쓰는 법

<DataGridTemplateColumn Header="판정" Width="96">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Border Style="{StaticResource Ubisam.Style.Tag.Accent}">
                <TextBlock Text="{Binding Verdict}" Style="{StaticResource Ubisam.Style.TagText.Accent}"/>
            </Border>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
스타일 쌍 쓰는 곳
Ubisam.Style.Tag.Accent + TagText.Accent 정상 · 완료 (양품, Info)
Ubisam.Style.Tag.Outline + TagText.Outline 주의 · 예외 (재검, Warn) — 외곽선만이라 눈에 걸립니다
Ubisam.Style.Tag + TagText 중립 · 대기

7. DataGrid 쓰는 법

<DataGrid ItemsSource="{Binding Measurements}" SelectedItem="{Binding Selected}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="시각" Width="96"
                            Binding="{Binding Timestamp, StringFormat=HH:mm:ss}" IsReadOnly="True"/>
        <DataGridTextColumn Header="레시피" Width="*" Binding="{Binding RecipeName}" IsReadOnly="True"/>
        <DataGridTextColumn Header="측정값" Width="118"
                            Binding="{Binding Value, StringFormat=N1}"
                            CellStyle="{StaticResource Ubisam.Style.NumericCell}"/>
    </DataGrid.Columns>
</DataGrid>

수치 열에는 반드시 CellStyle="{StaticResource Ubisam.Style.NumericCell}"을 주세요.

8. 확인

  1. 다크에서 콤보박스가 어두운 배경 + 각진 모서리인지 — 둥근 밝은 회색이면 §4의 머지 문제입니다
  2. 콤보박스를 열면 드롭다운이 입력창 바로 아래에 붙고, 선택 항목이 진한 파랑 + 흰 글자인지
  3. 입력창을 클릭하면 accent 테두리 + 옅은 글로우가 생기는지
  4. 주 버튼("저장")이 진한 파랑(#1E7FD4) + 흰 글자인지
  5. 체크박스 체크 표시가 흰색인지 — 검정이면 파일이 갱신되지 않은 것입니다
  6. 화면 배율 슬라이더의 왼쪽 구간만 파랗게 차고, 썸이 각진 세로 막대인지
  7. 스크롤바가 폭 10의 얇은 막대이고 hover에서 accent로 바뀌는지
  8. 표의 수치 열이 모두 우측 정렬인지
  9. 바탕(#23262B) → 판(#2A2E34) → 입력(#2F343A) 순으로 밝아지는 층이 눈에 보이는지
  10. 라이트 테마가 이전과 동일한지 — 새 키 2개가 추가됐을 뿐입니다

9. 하지 말 것

  • 모듈 View의 내용은 건드리지 마세요 — 테마와 스타일만 바꿉니다
  • 라운드 모서리를 되살리지 마세요 (CornerRadius는 태그의 3px만 예외)
  • Controls.xaml의 폰트 참조를 StaticResource로 바꾸지 마세요 — 머지 시점 때문에 로드가 실패합니다
  • TabManager, EventBus, ToastService, AuthService, LogService의 구조나 시그니처를 바꾸지 마세요