Skip to content

Commit 5072c99

Browse files
authored
Adds Random as the last Customization Studio theme dropdown option. Random clears and disables custom color overrides, emits theme=random in exported snippets, shows a caching warning, and renders a mixed-accent swatch (JhaSourav07#286)
## Description Fixes #ISSUE_NUMBER(JhaSourav07#144) Adds `Random` as the last option in the Customization Studio theme dropdown. When selected, it emits `theme=random`, clears and disables custom color overrides, shows a caching warning, and renders a mixed-accent swatch to communicate randomness. ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview Tested locally at: `http://localhost:3000/customize` ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally (`localhost:3000/customize`). - [x] I have run `npm run format:check`, `npm run lint`, and `npm run test` locally and resolved all errors. - [x] My commits follow the Conventional Commits format, e.g. `feat(customize): add random theme option`. - [x] I have updated `README.md` if I added a new theme or URL parameter. Not required for this change because `theme=random` already exists. - [x] I have started the repo. - [x] I have made sure that I have only one commit to merge in this PR. - [x] The SVG output matches the CommitPulse "premium quality" aesthetic standard. - [ ] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support..
2 parents 185d8c7 + 9736182 commit 5072c99

5 files changed

Lines changed: 56 additions & 20 deletions

File tree

app/customize/components/ControlsPanel.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ export function ControlsPanel({
125125
const hasOverrides = Boolean(bgHex || accentHex || textHex);
126126
const currentYear = new Date().getFullYear();
127127
const isAutoTheme = theme === 'auto';
128+
const isRandomTheme = theme === 'random';
129+
const disablesCustomColors = isAutoTheme || isRandomTheme;
128130

129131
return (
130132
<div>
@@ -171,12 +173,21 @@ export function ControlsPanel({
171173

172174
<div>
173175
<SectionLabel>Custom Color Overrides</SectionLabel>
174-
{isAutoTheme ? (
175-
<p className="text-[11px] text-white/30 mt-2 leading-relaxed">
176-
Custom colors are disabled for the <strong className="text-white/50">Auto</strong>{' '}
177-
theme. The badge switches between light and dark palettes automatically based on the
178-
viewer&apos;s system preference.
179-
</p>
176+
{disablesCustomColors ? (
177+
<div className="mt-2 flex flex-col gap-2">
178+
<p className="text-[11px] text-white/30 leading-relaxed">
179+
Custom colors are disabled for the{' '}
180+
<strong className="text-white/50">{isAutoTheme ? 'Auto' : 'Random'}</strong> theme.{' '}
181+
{isAutoTheme
182+
? "The badge switches between light and dark palettes automatically based on the viewer's system preference."
183+
: 'The badge chooses a different preset palette for each request.'}
184+
</p>
185+
{isRandomTheme && (
186+
<p className="rounded-lg border border-amber-400/15 bg-amber-400/5 px-3 py-2 text-[11px] leading-relaxed text-amber-200/70">
187+
Random changes on every page load and disables caching for the badge URL.
188+
</p>
189+
)}
190+
</div>
180191
) : (
181192
<>
182193
<p className="text-[11px] text-white/25 mb-3 leading-relaxed">

app/customize/components/ThemeQuickPresets.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ export function ThemeQuickPresets({ theme, onThemeChange }: ThemeQuickPresetsPro
397397
`}</style>
398398

399399
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px' }}>
400-
{THEME_KEYS.filter((key) => key !== 'auto').map((key) => {
400+
{THEME_KEYS.filter((key) => key !== 'auto' && key !== 'random').map((key) => {
401401
const t = themes[key as ThemeKey];
402402
if (!t) return null;
403403

app/customize/components/ThemeSelector.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export function ThemeSelector({
3535
onThemeChange: (theme: string) => void;
3636
}): ReactElement {
3737
const isAuto = theme === 'auto';
38+
const isRandom = theme === 'random';
39+
const randomAccentColors = [themes.neon.accent, themes.ocean.accent, themes.sunset.accent];
3840

3941
return (
4042
<div className="flex flex-col gap-1.5">
@@ -46,7 +48,11 @@ export function ThemeSelector({
4648
<StyledSelect id="theme-select" value={theme} onChange={onThemeChange}>
4749
{THEME_KEYS.map((key) => (
4850
<option key={key} value={key}>
49-
{key === 'auto' ? 'Auto (System)' : key.charAt(0).toUpperCase() + key.slice(1)}
51+
{key === 'auto'
52+
? 'Auto (System)'
53+
: key === 'random'
54+
? 'Random'
55+
: key.charAt(0).toUpperCase() + key.slice(1)}
5056
</option>
5157
))}
5258
</StyledSelect>
@@ -66,6 +72,20 @@ export function ThemeSelector({
6672
switches with OS theme
6773
</span>
6874
</>
75+
) : isRandom ? (
76+
<>
77+
{randomAccentColors.map((color, index) => (
78+
<span
79+
key={color}
80+
title={`Random accent sample ${index + 1}: #${color}`}
81+
className="w-5 h-5 rounded-full border border-white/10"
82+
style={{ backgroundColor: `#${color}` }}
83+
/>
84+
))}
85+
<span className="text-[11px] text-white/25 ml-1 self-center">
86+
changes on each load
87+
</span>
88+
</>
6989
) : (
7090
<>
7191
{(['bg', 'accent', 'text'] as const).map((prop) => {

app/customize/page.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ export default function CustomizePage(): ReactElement {
2626
const trimmedUsername = username.trim();
2727
const hasUsername = trimmedUsername.length > 0;
2828
const isAutoTheme = theme === 'auto';
29+
const isRandomTheme = theme === 'random';
30+
const skipsCustomColors = isAutoTheme || isRandomTheme;
2931

30-
// Clear custom hex overrides when switching to auto — fixed colors
31-
// conflict with the dual-palette prefers-color-scheme switching.
32+
// Clear custom hex overrides when switching to virtual themes because
33+
// fixed colors conflict with their palette-selection behavior.
3234
const handleThemeChange = useCallback((newTheme: string): void => {
3335
setTheme(newTheme);
34-
if (newTheme === 'auto') {
36+
if (newTheme === 'auto' || newTheme === 'random') {
3537
setBgHex('');
3638
setAccentHex('');
3739
setTextHex('');
@@ -47,9 +49,9 @@ export default function CustomizePage(): ReactElement {
4749
params.set('user', trimmedUsername);
4850
}
4951

50-
if (isAutoTheme) {
51-
// Auto always emits theme=auto — no custom color params
52-
params.set('theme', 'auto');
52+
if (skipsCustomColors) {
53+
// Virtual themes always emit theme=<name> and skip custom color params.
54+
params.set('theme', theme);
5355
} else {
5456
const hasCustomColors = bgHex || accentHex || textHex;
5557

@@ -72,7 +74,7 @@ export default function CustomizePage(): ReactElement {
7274
hasUsername,
7375
trimmedUsername,
7476
theme,
75-
isAutoTheme,
77+
skipsCustomColors,
7678
bgHex,
7779
accentHex,
7880
textHex,
@@ -260,7 +262,9 @@ export default function CustomizePage(): ReactElement {
260262

261263
<p className="mt-3 text-[11px] text-white/20 text-center">
262264
{hasUsername
263-
? 'Preview updates on every change. Hosted badge is cached at UTC midnight'
265+
? isRandomTheme
266+
? 'Random theme changes on every page load and disables caching'
267+
: 'Preview updates on every change. Hosted badge is cached at UTC midnight'
264268
: 'Add a username to enable live preview and export snippets'}
265269
</p>
266270
</div>

app/customize/types.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ export type ExportFormat = 'markdown' | 'html';
66

77
export type ThemeKey = Extract<keyof typeof themes, string>;
88

9-
// 'auto' is a virtual theme that uses CSS prefers-color-scheme to
10-
// switch between light and dark at runtime — it has no entry in the
11-
// themes record, so we prepend it manually.
12-
export const THEME_KEYS: (ThemeKey | 'auto')[] = ['auto', ...(Object.keys(themes) as ThemeKey[])];
9+
export type ThemeOption = ThemeKey | 'auto' | 'random';
10+
11+
// 'auto' and 'random' are virtual themes with no entries in the
12+
// themes record, so they are added around the concrete presets.
13+
export const THEME_KEYS: ThemeOption[] = ['auto', ...(Object.keys(themes) as ThemeKey[]), 'random'];
1314

1415
export const SPEEDS = [
1516
{ value: '4s', label: 'Fast (4s)' },

0 commit comments

Comments
 (0)