@simplysm/angular 14.2.1 → 14.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/controls/button/sd-anchor.d.ts +1 -1
  2. package/dist/controls/button/sd-anchor.d.ts.map +1 -1
  3. package/dist/controls/button/sd-anchor.js +2 -2
  4. package/dist/controls/button/sd-button.d.ts +1 -1
  5. package/dist/controls/button/sd-button.d.ts.map +1 -1
  6. package/dist/controls/button/sd-button.js +2 -2
  7. package/dist/controls/checkbox/sd-checkbox.d.ts +1 -1
  8. package/dist/controls/checkbox/sd-checkbox.d.ts.map +1 -1
  9. package/dist/controls/checkbox/sd-checkbox.js +2 -2
  10. package/dist/controls/checkbox/sd-switch.d.ts +1 -1
  11. package/dist/controls/checkbox/sd-switch.d.ts.map +1 -1
  12. package/dist/controls/checkbox/sd-switch.js +2 -2
  13. package/dist/controls/dropdown/sd-dropdown.d.ts +3 -0
  14. package/dist/controls/dropdown/sd-dropdown.d.ts.map +1 -1
  15. package/dist/controls/dropdown/sd-dropdown.js +29 -9
  16. package/dist/controls/input/sd-textarea.d.ts +1 -1
  17. package/dist/controls/input/sd-textarea.d.ts.map +1 -1
  18. package/dist/controls/input/sd-textarea.js +2 -2
  19. package/dist/controls/input/sd-textfield-type-handlers.js +1 -1
  20. package/dist/controls/input/sd-textfield.d.ts +1 -1
  21. package/dist/controls/input/sd-textfield.d.ts.map +1 -1
  22. package/dist/controls/input/sd-textfield.js +9 -10
  23. package/dist/controls/list/sd-list.d.ts.map +1 -1
  24. package/dist/controls/list/sd-list.js +4 -6
  25. package/dist/controls/select/sd-select-button.d.ts.map +1 -1
  26. package/dist/controls/select/sd-select-button.js +3 -5
  27. package/dist/controls/select/sd-select.js +2 -2
  28. package/dist/core/error-handler/sd-global-error-handler.plugin.js +1 -1
  29. package/dist/core/modal/sd-modal.provider.js +1 -1
  30. package/dist/core/provideSdAngular.d.ts.map +1 -1
  31. package/dist/core/provideSdAngular.js +1 -13
  32. package/dist/core/setupBgTheme.d.ts +1 -1
  33. package/dist/core/setupBgTheme.d.ts.map +1 -1
  34. package/dist/core/toast/sd-toast.js +2 -2
  35. package/dist/core/toast/sd-toast.provider.d.ts +1 -1
  36. package/dist/core/toast/sd-toast.provider.d.ts.map +1 -1
  37. package/dist/core/toast/sd-toast.provider.js +1 -1
  38. package/dist/data/crud/sd-base-container.d.ts +1 -1
  39. package/dist/data/crud/sd-base-container.d.ts.map +1 -1
  40. package/dist/data/crud/sd-base-container.js +2 -2
  41. package/dist/data/crud/sd-crud-detail.d.ts +1 -1
  42. package/dist/data/crud/sd-crud-detail.d.ts.map +1 -1
  43. package/dist/data/crud/sd-crud-list.d.ts +3 -3
  44. package/dist/data/crud/sd-crud-list.d.ts.map +1 -1
  45. package/dist/data/shared-data/sd-shared-data-select.d.ts +2 -2
  46. package/dist/data/shared-data/sd-shared-data-select.d.ts.map +1 -1
  47. package/dist/data/sheet/injectSheetDomAccessor.d.ts +1 -0
  48. package/dist/data/sheet/injectSheetDomAccessor.d.ts.map +1 -1
  49. package/dist/data/sheet/injectSheetDomAccessor.js +7 -0
  50. package/dist/data/sheet/injectSheetDragSelection.d.ts +18 -0
  51. package/dist/data/sheet/injectSheetDragSelection.d.ts.map +1 -0
  52. package/dist/data/sheet/injectSheetDragSelection.js +193 -0
  53. package/dist/data/sheet/sd-sheet.d.ts +3 -0
  54. package/dist/data/sheet/sd-sheet.d.ts.map +1 -1
  55. package/dist/data/sheet/sd-sheet.js +17 -6
  56. package/dist/data/state-preset/sd-state-preset.d.ts +2 -2
  57. package/dist/data/state-preset/sd-state-preset.d.ts.map +1 -1
  58. package/dist/features/editor/sd-tiptap-editor.js +2 -2
  59. package/dist/features/theme/sd-theme-provider.d.ts +0 -2
  60. package/dist/features/theme/sd-theme-provider.d.ts.map +1 -1
  61. package/dist/features/theme/sd-theme-provider.js +0 -5
  62. package/dist/features/theme/sd-theme-selector.d.ts +1 -1
  63. package/dist/features/theme/sd-theme-selector.d.ts.map +1 -1
  64. package/dist/features/theme/sd-theme-selector.js +5 -22
  65. package/dist/features/visual/sd-label.d.ts +1 -1
  66. package/dist/features/visual/sd-label.d.ts.map +1 -1
  67. package/dist/features/visual/sd-label.js +2 -2
  68. package/dist/features/visual/sd-note.d.ts +1 -1
  69. package/dist/features/visual/sd-note.d.ts.map +1 -1
  70. package/dist/features/visual/sd-note.js +2 -2
  71. package/dist/features/visual/sd-progress.d.ts +1 -1
  72. package/dist/features/visual/sd-progress.d.ts.map +1 -1
  73. package/dist/features/visual/sd-progress.js +2 -2
  74. package/dist/layout/sidebar/sd-sidebar.d.ts.map +1 -1
  75. package/dist/layout/sidebar/sd-sidebar.js +3 -5
  76. package/dist/layout/topbar/sd-topbar.d.ts.map +1 -1
  77. package/dist/layout/topbar/sd-topbar.js +3 -3
  78. package/package.json +26 -26
  79. package/scss/commons/_mixins.scss +1 -1
  80. package/scss/commons/_theme-variables.scss +0 -8
  81. package/scss/commons/_variables.scss +114 -121
  82. package/scss/themes/_variables-blueprint.scss +65 -63
  83. package/scss/themes/_variables-ide-dark.scss +166 -144
  84. package/src/controls/button/sd-anchor.ts +3 -3
  85. package/src/controls/button/sd-button.ts +1 -3
  86. package/src/controls/checkbox/sd-checkbox.ts +2 -10
  87. package/src/controls/checkbox/sd-switch.ts +2 -4
  88. package/src/controls/dropdown/sd-dropdown.ts +33 -9
  89. package/src/controls/input/sd-textarea.ts +2 -4
  90. package/src/controls/input/sd-textfield-type-handlers.ts +1 -1
  91. package/src/controls/input/sd-textfield.ts +9 -12
  92. package/src/controls/list/sd-list.ts +3 -5
  93. package/src/controls/select/sd-select-button.ts +2 -4
  94. package/src/controls/select/sd-select.ts +1 -1
  95. package/src/core/error-handler/sd-global-error-handler.plugin.ts +1 -1
  96. package/src/core/modal/sd-modal.provider.ts +1 -1
  97. package/src/core/provideSdAngular.ts +1 -15
  98. package/src/core/setupBgTheme.ts +1 -2
  99. package/src/core/toast/sd-toast.provider.ts +5 -3
  100. package/src/data/crud/sd-base-container.ts +1 -1
  101. package/src/data/sheet/injectSheetDomAccessor.ts +7 -0
  102. package/src/data/sheet/injectSheetDragSelection.ts +226 -0
  103. package/src/data/sheet/sd-sheet.ts +16 -3
  104. package/src/features/editor/sd-tiptap-editor.ts +1 -1
  105. package/src/features/theme/sd-theme-provider.ts +0 -9
  106. package/src/features/theme/sd-theme-selector.ts +1 -10
  107. package/src/features/visual/sd-label.ts +1 -3
  108. package/src/features/visual/sd-note.ts +1 -3
  109. package/src/features/visual/sd-progress.ts +2 -2
  110. package/src/layout/sidebar/sd-sidebar.ts +2 -3
  111. package/src/layout/topbar/sd-topbar.ts +1 -0
  112. package/styles.css +278 -567
  113. package/scss/commons/_colors.scss +0 -301
  114. package/scss/commons/_density.scss +0 -23
  115. package/scss/sd-tokens.md +0 -132
@@ -1,204 +1,207 @@
1
- // IDE 다크 테마 — VSCode "Dark 2026" 을 --sd-* 역할 토큰 값 맵으로만 구성(셀렉터 오버라이드 0).
2
- // 레퍼런스: microsoft/vscode extensions/theme-defaults/themes/2026-dark.json (v1.113 기본 테마)
3
- // 규칙(CONSTRAINT-007): --sd-color-* 팔레트 + 흰/검 알파(rgba white/black)만.
4
- // 이전 기준(FIND-015): 스케일 맵·ide-dark-surfaces 확정 상태를 라이트 기본값 등가 슬롯으로 역매핑.
5
- // 치수(gap·line-height·sheet pv/ph·topbar height) 밀도 그룹 소유(DEC-007) — 맵엔 없음.
1
+ // IDE 다크 테마 — VSCode "Dark 2026"(v1.113 기본, 2026-dark.json)을 --sd-* 역할 토큰 값 맵으로 구성.
2
+ // 원천: 리터럴 rgb(팔레트 배타 폐기 — 테마 값 자립). Dark 2026 은 near-black 3단 surface +
3
+ // solid 텍스트/보더 + 상태만 흰알파(하이브리드) + 죽인 teal 액센트가 핵심 의도.
4
+ // surface 3단: content #121314(editor) < canvas #191A1B(chrome/input) < elevated/overlay #202122(widget/menu).
5
+ // 치수(gap, radius, sheet pv/ph, topbar), 폰트(Sarasa Fixed K), 그림자(off) IDE 컨셉 항목 불변.
6
6
  $dark: (
7
7
  bg: (
8
- // 계층: canvas zinc-950 control zinc-900 elevated/overlay zinc-800
9
- canvas: var(--sd-color-zinc-950),
10
- control: var(--sd-color-zinc-900),
11
- elevated: var(--sd-color-zinc-800),
12
- overlay: var(--sd-color-zinc-800),
13
- sheet: var(--sd-color-zinc-950),
8
+ // 계층: content #121314(메인/에디터 — 제일 어두움) < canvas #191A1B(배경, 사이드바, 토프바) < elevated/overlay #202122(카드, 팝업)
9
+ // editor.background #121314 = 메인 콘텐츠(content) / sideBar.background #191A1B = 배경(canvas)
10
+ canvas: rgb(25, 26, 27),
11
+ // 콘텐츠 면 = editor.background #121314 (메인/에디터/시트셀/리스트 — 제일 어두움)
12
+ content: rgb(18, 19, 20),
13
+ elevated: rgb(32, 33, 34),
14
+ overlay: rgb(32, 33, 34),
15
+ sheet: rgb(18, 19, 20),
14
16
  inverse: white,
15
- // 필드 배경 — input.background 근사
16
- field: var(--sd-color-zinc-900),
17
- track: var(--sd-color-zinc-700),
18
- // 시맨틱 solid = 깊은 스텝(600) + hover 한 단계 밝게(500) — VSCode 방식(다크에서 500 배경은 과함)
17
+ // 필드 배경 — input.background #191A1B
18
+ field: rgb(25, 26, 27),
19
+ // 트랙 — inactive-selection #2C2D2E 근사
20
+ track: rgb(44, 45, 46),
21
+ // 노브 — 강조텍스트 #ededed 와 통일한 밝은 손잡이(트랙, success-solid 위 대비)
22
+ knob: rgb(237, 237, 237),
23
+ // 체크박스 박스 면 = checkbox.background #242526 (별도 면 — canvas 보다 밝음)
24
+ checkbox: rgb(36, 37, 38),
25
+ // 시맨틱 solid = 뮤트 중암 채움, hover 는 더 밝게(다크 hover 밝힘 — button #297AA0→#2B7DA3)
19
26
  "gray": (
20
- solid: var(--sd-color-zinc-400),
21
- solid-hover: var(--sd-color-zinc-300),
22
- subtle: var(--sd-color-zinc-800),
23
- subtle-hover: var(--sd-color-zinc-700),
27
+ solid: rgb(135, 136, 137),
28
+ solid-hover: rgb(160, 161, 162),
29
+ subtle: rgb(32, 33, 34),
30
+ subtle-hover: rgb(42, 43, 44),
24
31
  ),
25
32
  blue-gray: (
26
- solid: var(--sd-color-slate-400),
27
- solid-hover: var(--sd-color-slate-300),
28
- subtle: var(--sd-color-slate-800),
29
- subtle-hover: var(--sd-color-slate-700),
33
+ solid: rgb(100, 116, 139),
34
+ solid-hover: rgb(120, 136, 159),
35
+ subtle: rgb(28, 34, 44),
36
+ subtle-hover: rgb(38, 46, 58),
30
37
  ),
38
+ // 액센트 = 죽인 teal(button.background #297AA0 / hover #2B7DA3)
31
39
  primary: (
32
- solid: var(--sd-color-sky-600),
33
- solid-hover: var(--sd-color-sky-500),
34
- subtle: var(--sd-color-sky-950),
35
- subtle-hover: var(--sd-color-sky-900),
36
- ),
37
- secondary: (
38
- solid: var(--sd-color-sky-600),
39
- solid-hover: var(--sd-color-sky-500),
40
- subtle: var(--sd-color-zinc-900),
41
- subtle-hover: var(--sd-color-sky-900),
42
- ),
40
+ solid: rgb(41, 122, 160),
41
+ solid-hover: rgb(43, 125, 163),
42
+ subtle: rgb(20, 50, 63),
43
+ subtle-hover: rgb(26, 63, 78),
44
+ ),
43
45
  info: (
44
- solid: var(--sd-color-cyan-600),
45
- solid-hover: var(--sd-color-cyan-500),
46
- subtle: var(--sd-color-cyan-950),
47
- subtle-hover: var(--sd-color-cyan-900),
46
+ solid: rgb(57, 148, 188),
47
+ solid-hover: rgb(66, 158, 200),
48
+ subtle: rgb(16, 45, 60),
49
+ subtle-hover: rgb(22, 58, 76),
48
50
  ),
51
+ // success solid = 뮤트 다크그린(파생), tx 는 charts.green #86CF86
49
52
  success: (
50
- solid: var(--sd-color-green-600),
51
- solid-hover: var(--sd-color-green-500),
52
- subtle: var(--sd-color-green-950),
53
- subtle-hover: var(--sd-color-green-900),
54
- ),
53
+ solid: rgb(56, 125, 86),
54
+ solid-hover: rgb(64, 140, 97),
55
+ subtle: rgb(16, 45, 30),
56
+ subtle-hover: rgb(22, 58, 40),
57
+ ),
58
+ // warning = inputValidation.warningBorder #B89500
55
59
  warning: (
56
- solid: var(--sd-color-amber-600),
57
- solid-hover: var(--sd-color-amber-500),
58
- subtle: var(--sd-color-amber-950),
59
- subtle-hover: var(--sd-color-amber-900),
60
- ),
61
- // errorForeground #f48771 근사 = red-400 (solid400 기준·hover 500)
60
+ solid: rgb(184, 149, 0),
61
+ solid-hover: rgb(200, 163, 10),
62
+ subtle: rgb(48, 40, 4),
63
+ subtle-hover: rgb(60, 50, 6),
64
+ ),
65
+ // danger solid = inputValidation.errorBorder #BE1100, tx errorForeground #f48771
62
66
  danger: (
63
- solid: var(--sd-color-red-400),
64
- solid-hover: var(--sd-color-red-500),
65
- subtle: var(--sd-color-red-950),
66
- subtle-hover: var(--sd-color-red-900),
67
+ solid: rgb(190, 17, 0),
68
+ solid-hover: rgb(215, 25, 5),
69
+ subtle: rgb(58, 14, 8),
70
+ subtle-hover: rgb(74, 18, 10),
67
71
  ),
68
- // 오버레이·hover: list.hoverBackground #FFFFFF14 근사 알파
72
+ // 상태 오버레이 = 흰알파(list.hover #FFFFFF14 / active, focus #FFFFFF22)
69
73
  state: (
70
- hover: rgba(255, 255, 255, 0.05),
71
- active: rgba(255, 255, 255, 0.08),
72
- selected: rgba(255, 255, 255, 0.07),
74
+ hover: rgba(255, 255, 255, 0.08),
75
+ active: rgba(255, 255, 255, 0.13),
76
+ selected: rgba(255, 255, 255, 0.13),
77
+ // 포커스 없는 선택행 solid (list.inactiveSelection #2C2D2E)
78
+ selected-inactive: rgb(44, 45, 46),
73
79
  ),
74
- disabled: var(--sd-color-zinc-800),
80
+ // 드롭 자리, 텍스트 선택 (list.dropBackground #3994BC1A / editor.selection #276782dd)
81
+ drop-target: rgba(57, 148, 188, 0.1),
82
+ selection: rgba(39, 103, 130, 0.87),
83
+ // 비활성 = 가라앉음(canvas 보다 어둡게)
84
+ disabled: rgb(22, 23, 24),
75
85
  busy-overlay: rgba(0, 0, 0, 0.6),
76
- busy-indicator: rgba(255, 255, 255, 0.13),
86
+ // 진행/로딩 중립 (progressBar.background #878889)
87
+ busy-indicator: rgb(135, 136, 137)
77
88
  ),
78
- // 텍스트: 흰색 알파 스케일 (Dark 2026 근사 #ededed/#bfbfbf/#8c8c8c/#555)
89
+ // 텍스트 = solid 그레이(foreground #bfbfbf, description #8c8c8c, disabled #555, activeSelection #ededed)
79
90
  tx: (
80
- strong: rgba(255, 255, 255, 0.93),
81
- default: rgba(255, 255, 255, 0.75),
82
- muted: rgba(255, 255, 255, 0.55),
83
- faint: rgba(255, 255, 255, 0.33),
91
+ strong: rgb(237, 237, 237),
92
+ default: rgb(191, 191, 191),
93
+ muted: rgb(140, 140, 140),
94
+ faint: rgb(85, 85, 85),
84
95
  on-inverse: white,
85
96
  on-inverse-muted: rgba(255, 255, 255, 0.75),
86
97
  on-inverse-disabled: rgba(255, 255, 255, 0.55),
87
- disabled: rgba(255, 255, 255, 0.33),
88
- // 링크·단독 텍스트 = 깊은 (600), hover 시 500 (쨍함 억제)
89
- "gray": var(--sd-color-zinc-400),
90
- gray-hover: var(--sd-color-zinc-300),
91
- gray-subtle: var(--sd-color-zinc-200),
92
- blue-gray: var(--sd-color-slate-400),
93
- blue-gray-hover: var(--sd-color-slate-300),
94
- blue-gray-subtle: var(--sd-color-slate-200),
95
- primary: var(--sd-color-sky-600),
96
- primary-hover: var(--sd-color-sky-500),
97
- primary-subtle: var(--sd-color-sky-400),
98
- secondary: var(--sd-color-sky-600),
99
- secondary-hover: var(--sd-color-sky-500),
100
- secondary-subtle: var(--sd-color-sky-400),
101
- info: var(--sd-color-cyan-600),
102
- info-hover: var(--sd-color-cyan-500),
103
- info-subtle: var(--sd-color-cyan-300),
104
- success: var(--sd-color-green-600),
105
- success-hover: var(--sd-color-green-500),
106
- success-subtle: var(--sd-color-green-300),
107
- warning: var(--sd-color-amber-600),
108
- warning-hover: var(--sd-color-amber-500),
109
- warning-subtle: var(--sd-color-amber-300),
110
- danger: var(--sd-color-red-400),
111
- danger-hover: var(--sd-color-red-500),
112
- danger-subtle: var(--sd-color-red-300),
98
+ disabled: rgb(85, 85, 85),
99
+ // 시맨틱 텍스트 = VSCode 라이트 foreground(link #48A0C7, charts.* )
100
+ "gray": rgb(140, 140, 140),
101
+ gray-hover: rgb(191, 191, 191),
102
+ gray-subtle: rgb(191, 191, 191),
103
+ blue-gray: rgb(148, 163, 184),
104
+ blue-gray-hover: rgb(170, 183, 200),
105
+ blue-gray-subtle: rgb(170, 183, 200),
106
+ primary: rgb(72, 160, 199),
107
+ primary-hover: rgb(83, 165, 202),
108
+ primary-subtle: rgb(130, 197, 224),
109
+ info: rgb(87, 163, 248),
110
+ info-hover: rgb(120, 185, 250),
111
+ info-subtle: rgb(130, 190, 250),
112
+ success: rgb(134, 207, 134),
113
+ success-hover: rgb(160, 220, 160),
114
+ success-subtle: rgb(150, 215, 150),
115
+ warning: rgb(224, 185, 127),
116
+ warning-hover: rgb(232, 200, 150),
117
+ warning-subtle: rgb(232, 205, 160),
118
+ danger: rgb(244, 135, 113),
119
+ danger-hover: rgb(248, 160, 140),
120
+ danger-subtle: rgb(248, 170, 152),
113
121
  ),
114
- // 보더: 흰색 알파 헤어라인 (widget #2A2B2C·input #333536 근사)
122
+ // 보더 = solid 그레이(widget.border #2A2B2C, input.border #333536, checkbox #707070)
115
123
  bd: (
116
- hairline: rgba(255, 255, 255, 0.05),
117
- soft: rgba(255, 255, 255, 0.08),
118
- default: rgba(255, 255, 255, 0.1),
119
- strong: rgba(255, 255, 255, 0.12),
120
- emphasis: rgba(255, 255, 255, 0.16),
121
- field: rgba(255, 255, 255, 0.1),
122
- disabled: rgba(255, 255, 255, 0.08),
124
+ hairline: rgb(36, 37, 38),
125
+ soft: rgb(42, 43, 44),
126
+ default: rgb(42, 43, 44),
127
+ strong: rgb(51, 53, 54),
128
+ emphasis: rgb(112, 112, 112),
129
+ field: rgb(51, 53, 54),
130
+ disabled: rgb(42, 43, 44),
123
131
  "gray": (
124
- solid: var(--sd-color-zinc-400),
125
- solid-hover: var(--sd-color-zinc-300),
126
- subtle: var(--sd-color-zinc-600),
132
+ solid: rgb(135, 136, 137),
133
+ solid-hover: rgb(160, 161, 162),
134
+ subtle: rgb(51, 53, 54),
127
135
  ),
128
136
  blue-gray: (
129
- solid: var(--sd-color-slate-400),
130
- solid-hover: var(--sd-color-slate-300),
131
- subtle: var(--sd-color-slate-600),
137
+ solid: rgb(100, 116, 139),
138
+ solid-hover: rgb(120, 136, 159),
139
+ subtle: rgb(51, 53, 54),
132
140
  ),
133
141
  primary: (
134
- solid: var(--sd-color-sky-600),
135
- solid-hover: var(--sd-color-sky-500),
136
- subtle: var(--sd-color-sky-500),
137
- ),
138
- secondary: (
139
- solid: var(--sd-color-sky-600),
140
- solid-hover: var(--sd-color-sky-500),
141
- subtle: var(--sd-color-sky-500),
142
+ solid: rgb(41, 122, 160),
143
+ solid-hover: rgb(43, 125, 163),
144
+ subtle: rgb(38, 90, 110),
142
145
  ),
143
146
  info: (
144
- solid: var(--sd-color-cyan-600),
145
- solid-hover: var(--sd-color-cyan-500),
146
- subtle: var(--sd-color-cyan-600),
147
+ solid: rgb(57, 148, 188),
148
+ solid-hover: rgb(66, 158, 200),
149
+ subtle: rgb(30, 74, 96),
147
150
  ),
148
151
  success: (
149
- solid: var(--sd-color-green-600),
150
- solid-hover: var(--sd-color-green-500),
151
- subtle: var(--sd-color-green-600),
152
+ solid: rgb(56, 125, 86),
153
+ solid-hover: rgb(64, 140, 97),
154
+ subtle: rgb(34, 72, 52),
152
155
  ),
153
156
  warning: (
154
- solid: var(--sd-color-amber-600),
155
- solid-hover: var(--sd-color-amber-500),
156
- subtle: var(--sd-color-amber-600),
157
+ solid: rgb(184, 149, 0),
158
+ solid-hover: rgb(200, 163, 10),
159
+ subtle: rgb(90, 74, 10),
157
160
  ),
158
161
  danger: (
159
- solid: var(--sd-color-red-400),
160
- solid-hover: var(--sd-color-red-500),
161
- subtle: var(--sd-color-red-400),
162
+ solid: rgb(190, 17, 0),
163
+ solid-hover: rgb(215, 25, 5),
164
+ subtle: rgb(96, 30, 20),
162
165
  ),
163
166
  ),
167
+ // focusBorder #3994BCB3 (70% 알파 teal)
164
168
  focus-ring: (
165
- color: var(--sd-color-sky-600),
166
- ),
167
- // Dark 2026 scrollbarSlider.* 근사 (DEC-011)
169
+ color: rgba(57, 148, 188, 0.7),
170
+ ),
171
+ // scrollbarSlider #A8A9AA85/90
168
172
  scrollbar: (
169
- thumb: rgba(255, 255, 255, 0.1),
170
- thumb-hover: rgba(255, 255, 255, 0.16),
173
+ thumb: rgba(168, 169, 170, 0.52),
174
+ thumb-hover: rgba(168, 169, 170, 0.565),
171
175
  track: rgba(255, 255, 255, 0.03),
172
176
  ),
173
- // 그림자 전면 제거 — VSCode 경계를 보더로 세움
177
+ // 그림자 전면 제거 — dark JSON widget.shadow 부재, 계층을 휘도 3단으로 표현
174
178
  shadow: (
175
179
  size: 0,
176
180
  blur-mult: 0,
177
181
  ),
178
- // 다크는 그림자가 약해 카드 경계를 헤어라인으로, 포커스 상단 액센트 라인
179
- // (tab.activeBorderTop #3994BC 근사 = sky-500)
182
+ // 카드 경계 = solid widget.border #2A2B2C. focus 전체 outline(focus-ring)으로 — 상단 액센트 라인 제거
180
183
  card: (
181
- bd: rgba(255, 255, 255, 0.08),
182
- bd-active: var(--sd-color-sky-500),
184
+ bd: rgb(42, 43, 44),
185
+ bd-active: transparent,
183
186
  shadow: none,
184
187
  shadow-hover: none,
185
188
  ),
186
- // 오버레이 elevated 경계 — editorWidget/menu 배경 + widget.border #2A2B2C 근사
189
+ // 오버레이 경계 — editorWidget/menu 배경(#202122) + widget.border #2A2B2C solid
187
190
  modal: (
188
- bd: rgba(255, 255, 255, 0.22),
189
- header-bg: var(--sd-color-zinc-800),
190
- header-tx: rgba(255, 255, 255, 0.75),
191
- header-tx-muted: var(--sd-color-zinc-400),
191
+ bd: rgb(42, 43, 44),
192
+ header-bg: rgb(32, 33, 34),
193
+ header-tx: rgb(191, 191, 191),
194
+ header-tx-muted: rgb(140, 140, 140),
192
195
  ),
193
196
  dropdown: (
194
- bd: rgba(255, 255, 255, 0.22),
197
+ bd: rgb(42, 43, 44),
195
198
  ),
196
- // 권한표 그룹 행 — theme 원색 solid 를 무채 어두운 행으로
199
+ // 권한표 그룹 행 — 원색 solid 를 무채 어두운 행으로
197
200
  permission-group: (
198
- bg: var(--sd-color-zinc-800),
201
+ bg: rgb(32, 33, 34),
199
202
  tx: rgba(255, 255, 255, 0.75),
200
203
  ),
201
- // VSCode 수준 소형 라운드 — 버튼·인풋 2px, 위젯·메뉴 4px 급 (12px 기준)
204
+ // VSCode 수준 소형 라운드 — 버튼, 인풋 2px, 위젯, 메뉴 4px 급(12px 기준)
202
205
  radius: (
203
206
  xs: 0.0833rem,
204
207
  sm: 0.0833rem,
@@ -207,7 +210,26 @@ $dark: (
207
210
  xl: 0.3333rem,
208
211
  xxl: 0.5rem,
209
212
  ),
210
- // 폰트는 테마 컨셉이라 여기서만 Sarasa Fixed K 덮는다(라이트·블루프린트 불변)
213
+ // 치수 오밀조밀(구 밀도 compact 이관). 라이트, 블루프린트는 기본 치수 유지.
214
+ gap: (
215
+ xxs: 0.0833rem,
216
+ xs: 0.0833rem,
217
+ sm: 0.1667rem,
218
+ default: 0.3333rem,
219
+ lg: 0.4167rem,
220
+ xl: 0.6667rem,
221
+ xxl: 1rem,
222
+ ),
223
+ // line-height: 1.5em,
224
+ // line-height-strip-unit: 1.5,
225
+ sheet: (
226
+ pv: 0.0833rem,
227
+ ph: 0.1667rem,
228
+ ),
229
+ topbar: (
230
+ height: 2rem,
231
+ ),
232
+ // 폰트는 이 테마 컨셉이라 여기서만 Sarasa Fixed K 로 덮는다(라이트, 블루프린트 불변)
211
233
  font-family: '"Sarasa Fixed K", ui-monospace, monospace',
212
234
  font-family-field: '"Sarasa Fixed K", ui-monospace, monospace',
213
235
  font-family-monospace: '"Sarasa Fixed K", ui-monospace, monospace',
@@ -60,7 +60,7 @@ import {
60
60
  })
61
61
  export class SdAnchor {
62
62
  disabled = input(false, { transform: booleanAttribute });
63
- theme = input<
64
- "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray"
65
- >("primary");
63
+ theme = input<"primary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray">(
64
+ "primary",
65
+ );
66
66
  }
@@ -37,7 +37,7 @@ import { SdRipple } from "../../core/ripple/sd-ripple";
37
37
  padding: var(--sd-gap-sm) var(--sd-gap-lg);
38
38
  width: 100%;
39
39
 
40
- background-color: var(--sd-bg-control);
40
+ background-color: var(--sd-bg-content);
41
41
  border-color: var(--sd-bd-primary-solid);
42
42
  border-radius: var(--sd-radius-default);
43
43
 
@@ -184,7 +184,6 @@ export class SdButton {
184
184
  type = input<"button" | "submit">("button");
185
185
  theme = input<
186
186
  | "primary"
187
- | "secondary"
188
187
  | "info"
189
188
  | "success"
190
189
  | "warning"
@@ -193,7 +192,6 @@ export class SdButton {
193
192
  | "blue-gray"
194
193
  | "link"
195
194
  | "link-primary"
196
- | "link-secondary"
197
195
  | "link-info"
198
196
  | "link-success"
199
197
  | "link-warning"
@@ -135,7 +135,7 @@ import { tablerCheck } from "@ng-icons/tabler-icons";
135
135
 
136
136
  &[data-sd-theme="white"] {
137
137
  > ._indicator_rect {
138
- background-color: var(--sd-bg-control);
138
+ background-color: var(--sd-bg-checkbox);
139
139
  border-color: var(--sd-bd-field);
140
140
  }
141
141
 
@@ -279,15 +279,7 @@ export class SdCheckbox {
279
279
  inline = input(false, { transform: booleanAttribute });
280
280
  inset = input(false, { transform: booleanAttribute });
281
281
  theme = input<
282
- | "primary"
283
- | "secondary"
284
- | "info"
285
- | "success"
286
- | "warning"
287
- | "danger"
288
- | "gray"
289
- | "blue-gray"
290
- | "white"
282
+ "primary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray" | "white"
291
283
  >();
292
284
 
293
285
  contentStyle = input<string>();
@@ -56,7 +56,7 @@ import { setupModelHook } from "../../core/setupModelHook";
56
56
  height: calc(var(--sd-line-height) - var(--sd-gap-xs));
57
57
  border-radius: 100%;
58
58
 
59
- background-color: var(--sd-bg-control);
59
+ background-color: var(--sd-bg-knob);
60
60
 
61
61
  transition: transform var(--sd-animation-duration);
62
62
  }
@@ -123,9 +123,7 @@ export class SdSwitch {
123
123
  inset = input(false, { transform: booleanAttribute });
124
124
 
125
125
  size = input<"sm" | "lg">();
126
- theme = input<
127
- "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray"
128
- >();
126
+ theme = input<"primary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray">();
129
127
 
130
128
  constructor() {
131
129
  setupModelHook(this.value, this.canChangeFn);
@@ -42,7 +42,8 @@ export class SdDropdown {
42
42
  });
43
43
 
44
44
  private _popupEl?: HTMLElement;
45
- private _mouseoverEl?: HTMLElement;
45
+ /** `<svg>` 등 비-HTML 요소도 hover 대상이 되므로 `Element` 로 받는다. */
46
+ private _mouseoverEl?: Element;
46
47
  private _backdropEl?: HTMLElement;
47
48
  private readonly _isMobile;
48
49
  private readonly _mql: MediaQueryList;
@@ -88,7 +89,8 @@ export class SdDropdown {
88
89
  this._onDocumentScrollCapture(event);
89
90
  };
90
91
  const onMouseover = (event: Event) => {
91
- this._mouseoverEl = (event as MouseEvent).target as HTMLElement;
92
+ const target = (event as MouseEvent).target;
93
+ this._mouseoverEl = target instanceof Element ? target : undefined;
92
94
  };
93
95
  const onBlur = (event: Event) => {
94
96
  this._onDocumentBlurCapture(event as FocusEvent);
@@ -245,18 +247,27 @@ export class SdDropdown {
245
247
  }
246
248
  }
247
249
 
250
+ // 팝업 내부 요소가 disabled 로 전환되며 포커스를 잃은 경우.
251
+ // 브라우저가 렌더 도중 동기 blur 를 발사하는 경로이며, 사용자가 팝업을 벗어난 것이 아니다.
252
+ // 여기서 팝업을 닫으면 렌더 중 signal 쓰기가 되어 NG0600 이 발생한다.
253
+ const blurredEl = event.target;
254
+ if (
255
+ relatedTarget == null &&
256
+ blurredEl instanceof HTMLElement &&
257
+ (contentEl.contains(blurredEl) === true || popupEl.contains(blurredEl) === true) &&
258
+ blurredEl.matches(":disabled")
259
+ ) {
260
+ this._restoreFocus();
261
+ return;
262
+ }
263
+
248
264
  const mouseoverEl = this._mouseoverEl;
249
265
  if (
250
266
  relatedTarget == null &&
251
- mouseoverEl instanceof HTMLElement &&
267
+ mouseoverEl instanceof Element &&
252
268
  (contentEl.contains(mouseoverEl) === true || popupEl.contains(mouseoverEl) === true)
253
269
  ) {
254
- const tabbable = popupEl.findFirstTabbableChild();
255
- if (tabbable != null) {
256
- tabbable.focus();
257
- } else {
258
- contentEl.focus();
259
- }
270
+ this._restoreFocus();
260
271
  return;
261
272
  }
262
273
 
@@ -265,6 +276,19 @@ export class SdDropdown {
265
276
  }
266
277
  }
267
278
 
279
+ /** 팝업이 열린 상태를 유지해야 할 때 포커스를 팝업 안으로 되돌린다. */
280
+ private _restoreFocus(): void {
281
+ const popupEl = this._popupEl;
282
+ if (popupEl == null) return;
283
+
284
+ const tabbable = popupEl.findFirstTabbableChild();
285
+ if (tabbable != null) {
286
+ tabbable.focus();
287
+ } else {
288
+ this._elRef.nativeElement.focus();
289
+ }
290
+ }
291
+
268
292
  private _removePopup(): void {
269
293
  if (this._backdropEl != null) {
270
294
  this._backdropEl.remove();
@@ -160,7 +160,7 @@ import { setupInvalid } from "../../core/validation/setupInvalid";
160
160
  // inset(시트 셀 등)의 disabled 는 일반 콘텐츠처럼 표시(현행 유지)
161
161
  &[data-sd-inset="true"] {
162
162
  > ._contents {
163
- background-color: var(--sd-bg-control);
163
+ background-color: var(--sd-bg-content);
164
164
  color: var(--sd-tx-default);
165
165
  }
166
166
  }
@@ -190,9 +190,7 @@ export class SdTextarea {
190
190
  inset = input(false, { transform: booleanAttribute });
191
191
  size = input<"sm" | "lg">();
192
192
  validatorFn = input<(value: string | undefined) => string | undefined>();
193
- theme = input<
194
- "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray"
195
- >();
193
+ theme = input<"primary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray">();
196
194
  inputStyle = input<string>();
197
195
  inputClass = input<string>();
198
196
 
@@ -160,7 +160,7 @@ function createNumberHandler(): TextfieldTypeHandler {
160
160
  },
161
161
  isIncomplete(raw) {
162
162
  const inputValue = raw.replace(/[^0-9-.]/g, "");
163
- // 부호·소수점 형식은 갖췄으나 아직 완성되지 않은 진행 상태 ("-", "12.", ".", "-.")
163
+ // 부호, 소수점 형식은 갖췄으나 아직 완성되지 않은 진행 상태 ("-", "12.", ".", "-.")
164
164
  // Number("12.")===12 이므로 endsWith(".") 를 별도 판정 (parse 의 실패 조건과 정합)
165
165
  return (
166
166
  /^-?\d*\.?\d*$/.test(inputValue)
@@ -273,7 +273,7 @@ import { textfieldTypeHandlers, type SdTextfieldTypes } from "./sd-textfield-typ
273
273
  // inset(시트 셀 등)의 disabled 는 일반 콘텐츠처럼 표시(현행 유지)
274
274
  &[data-sd-inset="true"] {
275
275
  > ._contents {
276
- background-color: var(--sd-bg-control);
276
+ background-color: var(--sd-bg-content);
277
277
  color: var(--sd-tx-default);
278
278
  }
279
279
  }
@@ -326,13 +326,11 @@ export class SdTextfield<K extends keyof SdTextfieldTypes> {
326
326
  inline = input(false, { transform: booleanAttribute });
327
327
  inset = input(false, { transform: booleanAttribute });
328
328
  size = input<"sm" | "lg">();
329
- theme = input<
330
- "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray"
331
- >();
329
+ theme = input<"primary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray">();
332
330
 
333
331
  private readonly _inputElRef = viewChild<ElementRef<HTMLInputElement>>("inputEl");
334
332
 
335
- // 실제 input 의 브라우저 native 제약 위반(이메일 형식·날짜 미완성 등) 메시지. onInput/onBlur 에서 갱신.
333
+ // 실제 input 의 브라우저 native 제약 위반(이메일 형식, 날짜 미완성 등) 메시지. onInput/onBlur 에서 갱신.
336
334
  private readonly _nativeInvalidMessage = signal("");
337
335
 
338
336
  // IME 조합(한글 등) 중 여부. 조합 중에는 미완성 자모가 model 로 흐르지 않도록 갱신을 보류.
@@ -375,11 +373,11 @@ export class SdTextfield<K extends keyof SdTextfieldTypes> {
375
373
  if (this._composing) return;
376
374
 
377
375
  // 편집(포커스) 중이라도 변경 origin 을 구분한다.
378
- // 사용자 입력이 만든 DOM(정규화 차이·진행 중 입력 포함)은 보호하고,
379
- // 모델만 외부에서 바뀐 경우(프로그래밍적 리셋·prefill·표시옵션 변경)는 되써서 반영한다.
376
+ // 사용자 입력이 만든 DOM(정규화 차이, 진행 중 입력 포함)은 보호하고,
377
+ // 모델만 외부에서 바뀐 경우(프로그래밍적 리셋, prefill, 표시옵션 변경)는 되써서 반영한다.
380
378
  if (document.activeElement === inputEl) {
381
379
  const domParsed = this._handler().parse(inputEl.value, { format: this.format() });
382
- // DOM raw 가 현재 모델의 유효한 표현이면(사용자 입력 유래) 되쓰지 않아 캐럿·IME 보호
380
+ // DOM raw 가 현재 모델의 유효한 표현이면(사용자 입력 유래) 되쓰지 않아 캐럿, IME 보호
383
381
  const domReflectsModel =
384
382
  domParsed != null &&
385
383
  this._handler().toControlValue(domParsed, {
@@ -399,9 +397,8 @@ export class SdTextfield<K extends keyof SdTextfieldTypes> {
399
397
  }
400
398
 
401
399
  setupInvalid(() => {
402
- // 비활성·읽기전용 필드는 사용자가 편집할 없으므로 검증에서 제외 (네이티브 disabled 동작과 동일)
403
- if (this.disabled() || this.readonly()) return "";
404
-
400
+ // 검증은 편집 가능 여부와 무관하다. disabled, readonly 여도 값이 제약을 어기면 invalid 다.
401
+ // 값이 무의미한 맥락이면 소비자가 제약(required 등) 함께 내려야 한다.
405
402
  const value = this.value();
406
403
  const handlerErrors = this._handler().validate(value, {
407
404
  required: this.required(),
@@ -422,7 +419,7 @@ export class SdTextfield<K extends keyof SdTextfieldTypes> {
422
419
  }
423
420
  }
424
421
 
425
- // 브라우저 native 제약 위반(이메일 형식·날짜 미완성 등)을 우리 검증(빨간점)에 합류
422
+ // 브라우저 native 제약 위반(이메일 형식, 날짜 미완성 등)을 우리 검증(빨간점)에 합류
426
423
  const nativeMessage = this._nativeInvalidMessage();
427
424
  if (nativeMessage !== "") {
428
425
  errorMessages.push(nativeMessage);