@yeongjaeyou/claude-code-config 0.17.1 → 0.18.0

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.
@@ -0,0 +1,403 @@
1
+ # Refined Theme (Editorial Style)
2
+
3
+ A polished theme for Gradio applications with dark mode support. Avoids the generic AI aesthetic and delivers a professional tool-like experience.
4
+
5
+ ## Theme Class
6
+
7
+ ```python
8
+ from typing import Iterable
9
+ from gradio.themes import Soft
10
+ from gradio.themes.utils import colors, fonts, sizes
11
+
12
+
13
+ class RefinedTheme(Soft):
14
+ """Editorial/Documentation style theme with dark mode support
15
+
16
+ Features:
17
+ - No gradients, solid colors only
18
+ - Single accent color (Emerald)
19
+ - High contrast, professional look
20
+ - Pretendard font (Korean support)
21
+ - Built-in dark mode via _dark variants
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ *,
27
+ primary_hue: colors.Color | str = colors.zinc,
28
+ secondary_hue: colors.Color | str = colors.emerald,
29
+ neutral_hue: colors.Color | str = colors.zinc,
30
+ text_size: sizes.Size | str = sizes.text_md,
31
+ font: fonts.Font | str | Iterable[fonts.Font | str] = (
32
+ fonts.GoogleFont("Pretendard"),
33
+ "Pretendard",
34
+ "-apple-system",
35
+ "BlinkMacSystemFont",
36
+ "system-ui",
37
+ "sans-serif",
38
+ ),
39
+ font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
40
+ fonts.GoogleFont("JetBrains Mono"),
41
+ "ui-monospace",
42
+ "monospace",
43
+ ),
44
+ ):
45
+ super().__init__(
46
+ primary_hue=primary_hue,
47
+ secondary_hue=secondary_hue,
48
+ neutral_hue=neutral_hue,
49
+ text_size=text_size,
50
+ font=font,
51
+ font_mono=font_mono,
52
+ )
53
+ super().set(
54
+ # === Light Mode (Default) ===
55
+ body_background_fill="#fafafa",
56
+ background_fill_primary="#ffffff",
57
+ background_fill_secondary="#f4f4f5",
58
+
59
+ # === Dark Mode Variants ===
60
+ body_background_fill_dark="#18181b",
61
+ background_fill_primary_dark="#27272a",
62
+ background_fill_secondary_dark="#3f3f46",
63
+
64
+ # Text colors
65
+ body_text_color="*neutral_800",
66
+ body_text_color_dark="#fafafa",
67
+ block_title_text_color="*neutral_800",
68
+ block_title_text_color_dark="#fafafa",
69
+
70
+ # Buttons - solid colors (no gradients)
71
+ button_primary_background_fill="*secondary_600",
72
+ button_primary_background_fill_hover="*secondary_700",
73
+ button_primary_text_color="white",
74
+ button_primary_background_fill_dark="*secondary_500",
75
+ button_primary_background_fill_hover_dark="*secondary_600",
76
+
77
+ button_secondary_background_fill="*neutral_100",
78
+ button_secondary_background_fill_hover="*neutral_200",
79
+ button_secondary_background_fill_dark="*neutral_700",
80
+ button_secondary_background_fill_hover_dark="*neutral_600",
81
+ button_secondary_text_color_dark="#fafafa",
82
+
83
+ # Minimal styling
84
+ block_border_width="1px",
85
+ block_border_color="*neutral_200",
86
+ block_border_color_dark="*neutral_700",
87
+ block_shadow="none",
88
+ button_shadow="none",
89
+ button_primary_shadow="none",
90
+
91
+ # Margins and spacing
92
+ spacing_lg="1.5rem",
93
+ spacing_md="1rem",
94
+
95
+ # Title styling
96
+ block_title_text_weight="600",
97
+ block_title_text_size="*text_md",
98
+
99
+ # Input fields
100
+ input_background_fill="*neutral_50",
101
+ input_background_fill_dark="*neutral_800",
102
+ input_border_color="*neutral_300",
103
+ input_border_color_dark="*neutral_600",
104
+ input_border_width="1px",
105
+
106
+ # Accent colors - for tabs, links, and interactive elements
107
+ # Use secondary (emerald) instead of primary (zinc) for visibility
108
+ color_accent="*secondary_500",
109
+ color_accent_soft="*secondary_100",
110
+ color_accent_soft_dark="*secondary_800",
111
+ border_color_accent="*secondary_400",
112
+ border_color_accent_dark="*secondary_600",
113
+ )
114
+
115
+
116
+ # Create theme instance
117
+ refined_theme = RefinedTheme()
118
+ ```
119
+
120
+ ## CSS Styles
121
+
122
+ ```python
123
+ css = """
124
+ /* Container */
125
+ #col-container {
126
+ margin: 0 auto;
127
+ max-width: 1000px;
128
+ }
129
+
130
+ /* Title */
131
+ #main-title h1 {
132
+ font-size: 1.75rem !important;
133
+ font-weight: 600 !important;
134
+ }
135
+
136
+ /* Smooth theme transition */
137
+ body, .gradio-container {
138
+ transition: background-color 0.2s ease, color 0.2s ease;
139
+ }
140
+
141
+ /* Buttons */
142
+ .submit-btn {
143
+ font-weight: 500 !important;
144
+ }
145
+
146
+ /* Theme toggle button (native HTML button via gr.HTML) */
147
+ .theme-toggle-btn {
148
+ min-width: 40px;
149
+ height: 40px;
150
+ padding: 8px;
151
+ border: 1px solid var(--border-color-primary);
152
+ border-radius: 8px;
153
+ background-color: var(--background-fill-primary);
154
+ color: var(--body-text-color);
155
+ cursor: pointer;
156
+ display: inline-flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ transition: border-color 0.2s ease, background-color 0.2s ease;
160
+ }
161
+ .theme-toggle-btn:hover {
162
+ border-color: var(--color-accent);
163
+ background-color: var(--background-fill-secondary);
164
+ }
165
+
166
+ /* Moon icon (light mode - shows moon to switch to dark) */
167
+ #theme-toggle .icon-moon { display: inline-flex; }
168
+ #theme-toggle .icon-sun { display: none; }
169
+
170
+ /* Sun icon (dark mode - shows sun to switch to light) */
171
+ .dark #theme-toggle .icon-moon { display: none; }
172
+ .dark #theme-toggle .icon-sun { display: inline-flex; }
173
+
174
+ /* Text areas */
175
+ textarea {
176
+ font-size: 0.9rem !important;
177
+ }
178
+
179
+ /* Labels */
180
+ .label-wrap {
181
+ font-weight: 500 !important;
182
+ }
183
+ """
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Dark Mode Toggle
189
+
190
+ ### Method 1: gr.HTML with Native Button (Recommended for SVG Icons)
191
+
192
+ **IMPORTANT**: In Gradio 5.x, `gr.Button` does NOT render HTML in the `value` parameter - it escapes HTML to text. Use `gr.HTML` with a native `<button>` element for SVG icon toggles.
193
+
194
+ ```python
195
+ import gradio as gr
196
+
197
+ # Native HTML button with SVG icons (CSS controls visibility)
198
+ THEME_TOGGLE_HTML = """
199
+ <button id="theme-toggle" class="theme-toggle-btn" type="button" aria-label="Toggle theme">
200
+ <span class="icon-moon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg></span>
201
+ <span class="icon-sun"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg></span>
202
+ </button>
203
+ """
204
+ ```
205
+
206
+ ### JavaScript for Theme Toggle
207
+
208
+ ```python
209
+ # Initialize theme on page load AND attach click handler
210
+ INIT_THEME_JS = """
211
+ () => {
212
+ const saved = localStorage.getItem('theme');
213
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
214
+ const shouldBeDark = saved === 'dark' || (!saved && prefersDark);
215
+
216
+ if (shouldBeDark) {
217
+ document.documentElement.classList.add('dark');
218
+ }
219
+
220
+ // Attach click handler to theme toggle button
221
+ const toggleBtn = document.getElementById('theme-toggle');
222
+ if (toggleBtn) {
223
+ toggleBtn.addEventListener('click', (e) => {
224
+ e.preventDefault();
225
+ document.documentElement.classList.toggle('dark');
226
+ const isDark = document.documentElement.classList.contains('dark');
227
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
228
+ });
229
+ }
230
+ }
231
+ """
232
+ ```
233
+
234
+ ### Usage Pattern
235
+
236
+ **Important**: Gradio Row wraps each child in a div with `flex: 1 1 0%` by default.
237
+ To push the toggle to the right edge, CSS wrapper override is required.
238
+
239
+ **Additional CSS for header layout:**
240
+ ```css
241
+ /* Header row - prevent wrap, center vertically */
242
+ .row.header-row {
243
+ align-items: center !important;
244
+ flex-wrap: nowrap !important;
245
+ }
246
+
247
+ /* Title wrapper - expand to fill space */
248
+ #main-title {
249
+ flex: 1 1 auto !important;
250
+ min-width: 0 !important;
251
+ }
252
+
253
+ /* Toggle wrapper - fixed size */
254
+ .header-row > .theme-toggle-container {
255
+ flex: 0 0 auto !important;
256
+ min-width: 0 !important;
257
+ width: auto !important;
258
+ }
259
+ ```
260
+
261
+ **Layout code:**
262
+ ```python
263
+ import gradio as gr
264
+
265
+ with gr.Blocks(theme=refined_theme, css=css) as demo:
266
+ with gr.Column(elem_id="col-container"):
267
+ # Header with theme toggle (use elem_classes for CSS targeting)
268
+ with gr.Row(elem_classes=["header-row"]):
269
+ gr.Markdown("# App Title", elem_id="main-title")
270
+ gr.HTML(value=THEME_TOGGLE_HTML, elem_classes=["theme-toggle-container"])
271
+
272
+ # Your UI components here...
273
+ input_image = gr.Image(label="Input Image", type="pil")
274
+ submit_btn = gr.Button("Run", variant="primary")
275
+
276
+ # Initialize theme on load (includes click handler)
277
+ demo.load(fn=None, js=INIT_THEME_JS)
278
+ ```
279
+
280
+ **Common pitfalls:**
281
+ - `Row > Column(scale=0, min_width=0)` pattern can cause width=0px issues
282
+ - `margin-left: auto` on inner element won't work (targets wrong div)
283
+ - Without `flex-wrap: nowrap`, toggle may wrap to next line
284
+
285
+ ---
286
+
287
+ ### Method 2: Text-Based Toggle (Simplest)
288
+
289
+ For minimal implementation without icons:
290
+
291
+ ```python
292
+ THEME_TOGGLE_JS_TEXT = """
293
+ () => {
294
+ const html = document.documentElement;
295
+ html.classList.toggle('dark');
296
+ const isDark = html.classList.contains('dark');
297
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
298
+
299
+ const btn = document.querySelector('#theme-toggle button');
300
+ if (btn) {
301
+ btn.textContent = isDark ? 'Light' : 'Dark';
302
+ }
303
+ }
304
+ """
305
+
306
+ # Usage
307
+ theme_btn = gr.Button("Dark", elem_id="theme-toggle", size="sm")
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Alternative Accent Colors
313
+
314
+ Single accent color options to use instead of Emerald:
315
+
316
+ | Color | Gradio Color | Use Case |
317
+ |-------|--------------|----------|
318
+ | Amber | `colors.amber` | Warm and energetic feel |
319
+ | Sky | `colors.sky` | Calm and trustworthy feel |
320
+ | Rose | `colors.rose` | Soft and approachable feel |
321
+ | Teal | `colors.teal` | Modern and sophisticated feel |
322
+
323
+ **Note**: Use only one accent color per application. Mixing multiple colors creates a generic AI aesthetic.
324
+
325
+ ---
326
+
327
+ ## Complete Example
328
+
329
+ ```python
330
+ import gradio as gr
331
+ from gradio.themes import Soft
332
+ from gradio.themes.utils import colors, fonts
333
+
334
+
335
+ class RefinedTheme(Soft):
336
+ def __init__(self):
337
+ super().__init__(
338
+ primary_hue=colors.zinc,
339
+ secondary_hue=colors.emerald,
340
+ neutral_hue=colors.zinc,
341
+ font=(fonts.GoogleFont("Pretendard"), "system-ui", "sans-serif"),
342
+ font_mono=(fonts.GoogleFont("JetBrains Mono"), "monospace"),
343
+ )
344
+ super().set(
345
+ body_background_fill="#fafafa",
346
+ body_background_fill_dark="#18181b",
347
+ background_fill_primary="#ffffff",
348
+ background_fill_primary_dark="#27272a",
349
+ body_text_color="*neutral_800",
350
+ body_text_color_dark="#fafafa",
351
+ button_primary_background_fill="*secondary_600",
352
+ button_primary_background_fill_dark="*secondary_500",
353
+ block_shadow="none",
354
+ button_shadow="none",
355
+ )
356
+
357
+
358
+ THEME_ICONS = '''
359
+ <span class="icon-moon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg></span>
360
+ <span class="icon-sun"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg></span>
361
+ '''
362
+
363
+ css = """
364
+ #col-container { margin: 0 auto; max-width: 1000px; }
365
+ #theme-toggle button { min-width: 40px !important; padding: 8px !important; }
366
+ #theme-toggle .icon-moon { display: inline-block; }
367
+ #theme-toggle .icon-sun { display: none; }
368
+ .dark #theme-toggle .icon-moon { display: none; }
369
+ .dark #theme-toggle .icon-sun { display: inline-block; }
370
+ body { transition: background-color 0.2s ease; }
371
+ """
372
+
373
+ TOGGLE_JS = """() => {
374
+ document.documentElement.classList.toggle('dark');
375
+ const isDark = document.documentElement.classList.contains('dark');
376
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
377
+ }"""
378
+
379
+ INIT_JS = """() => {
380
+ const saved = localStorage.getItem('theme');
381
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
382
+ if (saved === 'dark' || (!saved && prefersDark)) {
383
+ document.documentElement.classList.add('dark');
384
+ }
385
+ }"""
386
+
387
+ theme = RefinedTheme()
388
+
389
+ with gr.Blocks(theme=theme, css=css) as demo:
390
+ with gr.Column(elem_id="col-container"):
391
+ with gr.Row():
392
+ gr.Markdown("# My App")
393
+ theme_btn = gr.Button(THEME_ICONS, elem_id="theme-toggle", size="sm")
394
+
395
+ image = gr.Image(label="Upload", type="pil")
396
+ btn = gr.Button("Run", variant="primary")
397
+
398
+ theme_btn.click(fn=None, js=TOGGLE_JS)
399
+ demo.load(fn=None, js=INIT_JS)
400
+
401
+ if __name__ == "__main__":
402
+ demo.queue(max_size=30).launch(mcp_server=True, ssr_mode=False, show_error=True)
403
+ ```