Skip to content

Commit 0139cfc

Browse files
authored
Merge pull request #113 from brionmario/next-fixes-july-28
feat: add support to define `component` level overrides from the `Theme`
2 parents 19cf975 + 17380f7 commit 0139cfc

File tree

10 files changed

+157
-13
lines changed

10 files changed

+157
-13
lines changed

.changeset/sweet-geese-guess.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@asgardeo/javascript': patch
3+
'@asgardeo/react': patch
4+
---
5+
6+
Add component-specific overrides.

packages/javascript/src/theme/createTheme.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,43 @@ const toCssVariables = (theme: ThemeConfig): Record<string, string> => {
449449
});
450450
}
451451

452+
/* |---------------------------------------------------------------| */
453+
/* | Components | */
454+
/* |---------------------------------------------------------------| */
455+
456+
// Button Overrides
457+
if (theme.components?.Button?.styleOverrides?.root?.borderRadius) {
458+
cssVars[`--${prefix}-component-button-root-borderRadius`] =
459+
theme.components.Button.styleOverrides.root.borderRadius;
460+
}
461+
462+
// Field Overrides (Parent of `TextField`, `DatePicker`, `OtpField`, `Select`, etc.)
463+
if (theme.components?.Field?.styleOverrides?.root?.borderRadius) {
464+
cssVars[`--${prefix}-component-field-root-borderRadius`] = theme.components.Field.styleOverrides.root.borderRadius;
465+
}
466+
452467
return cssVars;
453468
};
454469

455470
const toThemeVars = (theme: ThemeConfig): ThemeVars => {
456471
const prefix = theme.cssVarPrefix || VendorConstants.VENDOR_PREFIX;
457472

473+
const componentVars: ThemeVars['components'] = {};
474+
if (theme.components?.Button?.styleOverrides?.root?.borderRadius) {
475+
componentVars.Button = {
476+
root: {
477+
borderRadius: `var(--${prefix}-component-button-root-borderRadius)`,
478+
},
479+
};
480+
}
481+
if (theme.components?.Field?.styleOverrides?.root?.borderRadius) {
482+
componentVars.Field = {
483+
root: {
484+
borderRadius: `var(--${prefix}-component-field-root-borderRadius)`,
485+
},
486+
};
487+
}
488+
458489
const themeVars: ThemeVars = {
459490
colors: {
460491
action: {
@@ -558,6 +589,10 @@ const toThemeVars = (theme: ThemeConfig): ThemeVars => {
558589
});
559590
}
560591

592+
if (Object.keys(componentVars).length > 0) {
593+
themeVars.components = componentVars;
594+
}
595+
561596
return themeVars;
562597
};
563598

packages/javascript/src/theme/types.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,47 @@ export interface ThemeColors {
101101
};
102102
}
103103

104+
export interface ThemeComponentStyleOverrides {
105+
/**
106+
* Style overrides for the root element or slots.
107+
* Example: { root: { borderRadius: '8px' } }
108+
*/
109+
root?: Record<string, any>;
110+
[slot: string]: Record<string, any> | undefined;
111+
}
112+
113+
export interface ThemeComponents {
114+
Button?: {
115+
styleOverrides?: {
116+
root?: {
117+
borderRadius?: string;
118+
[key: string]: any;
119+
};
120+
[slot: string]: Record<string, any> | undefined;
121+
};
122+
defaultProps?: Record<string, any>;
123+
variants?: Array<Record<string, any>>;
124+
};
125+
Field?: {
126+
styleOverrides?: {
127+
root?: {
128+
borderRadius?: string;
129+
[key: string]: any;
130+
};
131+
[slot: string]: Record<string, any> | undefined;
132+
};
133+
defaultProps?: Record<string, any>;
134+
variants?: Array<Record<string, any>>;
135+
};
136+
[componentName: string]:
137+
| {
138+
styleOverrides?: ThemeComponentStyleOverrides;
139+
defaultProps?: Record<string, any>;
140+
variants?: Array<Record<string, any>>;
141+
}
142+
| undefined;
143+
}
144+
104145
export interface ThemeConfig {
105146
borderRadius: {
106147
large: string;
@@ -148,6 +189,32 @@ export interface ThemeConfig {
148189
* @default 'asgardeo' (from VendorConstants.VENDOR_PREFIX)
149190
*/
150191
cssVarPrefix?: string;
192+
/**
193+
* Component style overrides
194+
*/
195+
components?: ThemeComponents;
196+
}
197+
198+
export interface ThemeComponentVars {
199+
Button?: {
200+
root?: {
201+
borderRadius?: string;
202+
[key: string]: any;
203+
};
204+
[slot: string]: Record<string, any> | undefined;
205+
};
206+
Field?: {
207+
root?: {
208+
borderRadius?: string;
209+
[key: string]: any;
210+
};
211+
[slot: string]: Record<string, any> | undefined;
212+
};
213+
[componentName: string]:
214+
| {
215+
[slot: string]: Record<string, any> | undefined;
216+
}
217+
| undefined;
151218
}
152219

153220
export interface ThemeVars {
@@ -266,6 +333,10 @@ export interface ThemeVars {
266333
}
267334
| undefined;
268335
};
336+
/**
337+
* Component CSS variable references (e.g., for overrides)
338+
*/
339+
components?: ThemeComponentVars;
269340
}
270341

271342
export interface Theme extends ThemeConfig {

packages/javascript/src/utils/transformBrandingPreferenceToTheme.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const transformThemeVariant = (themeVariant: ThemeVariant, isDark = false): Part
4949
const inputs = themeVariant.inputs;
5050
const images = themeVariant.images;
5151

52-
return {
52+
const config: Partial<ThemeConfig> = {
5353
colors: {
5454
action: {
5555
active: isDark ? 'rgba(255, 255, 255, 0.70)' : 'rgba(0, 0, 0, 0.54)',
@@ -111,13 +111,6 @@ const transformThemeVariant = (themeVariant: ThemeVariant, isDark = false): Part
111111
dark: (colors?.alerts?.warning as ColorVariant)?.dark || (colors?.alerts?.warning as ColorVariant)?.main,
112112
},
113113
},
114-
// Extract border radius from buttons or inputs
115-
borderRadius: {
116-
small: buttons?.primary?.base?.border?.borderRadius || inputs?.base?.border?.borderRadius,
117-
medium: buttons?.secondary?.base?.border?.borderRadius,
118-
large: buttons?.externalConnection?.base?.border?.borderRadius,
119-
},
120-
// Extract and transform images
121114
images: {
122115
favicon: images?.favicon
123116
? {
@@ -135,6 +128,38 @@ const transformThemeVariant = (themeVariant: ThemeVariant, isDark = false): Part
135128
: undefined,
136129
},
137130
};
131+
132+
/* |---------------------------------------------------------------| */
133+
/* | Components | */
134+
/* |---------------------------------------------------------------| */
135+
136+
const buttonBorderRadius = buttons?.primary?.base?.border?.borderRadius;
137+
const fieldBorderRadius = inputs?.base?.border?.borderRadius;
138+
139+
if (buttonBorderRadius || fieldBorderRadius) {
140+
config.components = {
141+
...(buttonBorderRadius && {
142+
Button: {
143+
styleOverrides: {
144+
root: {
145+
borderRadius: buttonBorderRadius,
146+
},
147+
},
148+
},
149+
}),
150+
...(fieldBorderRadius && {
151+
Field: {
152+
styleOverrides: {
153+
root: {
154+
borderRadius: fieldBorderRadius,
155+
},
156+
},
157+
},
158+
}),
159+
};
160+
}
161+
162+
return config;
138163
};
139164

140165
/**

packages/react/src/components/primitives/Button/Button.styles.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ const useStyles = (
5353
align-items: center;
5454
justify-content: center;
5555
gap: calc(${theme.vars.spacing.unit} * 1);
56-
border-radius: ${shape === 'round' ? '50%' : theme.vars.borderRadius.medium};
56+
border-radius: ${shape === 'round'
57+
? '50%'
58+
: theme.vars.components?.Button?.root?.borderRadius || theme.vars.borderRadius.medium};
5759
font-weight: 500;
5860
cursor: ${disabled || loading ? 'not-allowed' : 'pointer'};
5961
outline: none;

packages/react/src/components/primitives/DatePicker/DatePicker.styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const useStyles = (theme: Theme, colorScheme: string, hasError: boolean, disable
3434
width: 100%;
3535
padding: ${theme.vars.spacing.unit} calc(${theme.vars.spacing.unit} * 1.5);
3636
border: 1px solid ${theme.vars.colors.border};
37-
border-radius: ${theme.vars.borderRadius.medium};
37+
border-radius: ${theme.vars.components?.Field?.root?.borderRadius || theme.vars.borderRadius.medium};
3838
font-size: 1rem;
3939
color: ${theme.vars.colors.text.primary};
4040
background-color: ${theme.vars.colors.background.surface};

packages/react/src/components/primitives/OtpField/OtpField.styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const useStyles = (
5454
font-size: ${theme.vars.typography.fontSizes.xl};
5555
font-weight: 500;
5656
border: 2px solid ${hasError ? theme.vars.colors.error.main : theme.vars.colors.border};
57-
border-radius: ${theme.vars.borderRadius.medium};
57+
border-radius: ${theme.vars.components?.Field?.root?.borderRadius || theme.vars.borderRadius.medium};
5858
color: ${theme.vars.colors.text.primary};
5959
background-color: ${disabled ? theme.vars.colors.background.disabled : theme.vars.colors.background.surface};
6060
outline: none;

packages/react/src/components/primitives/Select/Select.styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const useStyles = (theme: Theme, colorScheme: string, disabled: boolean, hasErro
3737
width: 100%;
3838
padding: ${theme.vars.spacing.unit} calc(${theme.vars.spacing.unit} * 1.5);
3939
border: 1px solid ${hasError ? theme.vars.colors.error.main : theme.vars.colors.border};
40-
border-radius: ${theme.vars.borderRadius.medium};
40+
border-radius: ${theme.vars.components?.Field?.root?.borderRadius || theme.vars.borderRadius.medium};
4141
font-size: ${theme.vars.typography.fontSizes.md};
4242
color: ${theme.vars.colors.text.primary};
4343
background-color: ${disabled ? theme.vars.colors.background.disabled : theme.vars.colors.background.surface};

packages/react/src/components/primitives/TextField/TextField.styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const useStyles = (
5454
width: 100%;
5555
padding: ${theme.vars.spacing.unit} ${rightPadding} ${theme.vars.spacing.unit} ${leftPadding};
5656
border: 1px solid ${hasError ? theme.vars.colors.error.main : theme.vars.colors.border};
57-
border-radius: ${theme.vars.borderRadius.medium};
57+
border-radius: ${theme.vars.components?.Field?.root?.borderRadius || theme.vars.borderRadius.medium};
5858
font-size: ${theme.vars.typography.fontSizes.md};
5959
color: ${theme.vars.colors.text.primary};
6060
background-color: ${disabled ? theme.vars.colors.background.disabled : theme.vars.colors.background.surface};

packages/react/src/contexts/Theme/ThemeProvider.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ const ThemeProvider: FC<PropsWithChildren<ThemeProviderProps>> = ({
175175
shadows: brandingTheme.shadows,
176176
spacing: brandingTheme.spacing,
177177
images: brandingTheme.images,
178+
components: brandingTheme.components,
178179
};
179180

180181
// Merge branding theme with user-provided theme config
@@ -202,6 +203,10 @@ const ThemeProvider: FC<PropsWithChildren<ThemeProviderProps>> = ({
202203
...brandingThemeConfig.images,
203204
...themeConfig?.images,
204205
},
206+
components: {
207+
...brandingThemeConfig.components,
208+
...themeConfig?.components,
209+
},
205210
};
206211
}, [inheritFromBranding, brandingTheme, themeConfig]);
207212

0 commit comments

Comments
 (0)