Skip to content

Commit 6353af5

Browse files
committed
Feat: Added foundational components and setup for Input component
1 parent 1479f9b commit 6353af5

File tree

21 files changed

+914
-0
lines changed

21 files changed

+914
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/* Form CSS Module */
2+
/* Styles for Form components (Label, Hint, etc.) */
3+
4+
/* ============================================
5+
FORM LABEL STYLES
6+
============================================ */
7+
8+
.formLabel {
9+
display: flex;
10+
flex-direction: row;
11+
align-items: center;
12+
flex-shrink: 0;
13+
}
14+
15+
.formLabel--top {
16+
width: 100%;
17+
flex-direction: row;
18+
justify-content: space-between;
19+
}
20+
21+
.formLabel--left {
22+
flex-direction: column;
23+
align-items: flex-start;
24+
}
25+
26+
/* Label width for left position */
27+
.formLabel--leftWidth-xsmall,
28+
.formLabel--leftWidth-small,
29+
.formLabel--leftWidth-medium {
30+
width: 120px;
31+
}
32+
33+
.formLabel--leftWidth-large {
34+
width: 176px;
35+
}
36+
37+
/* Label margin right for left position */
38+
.formLabel--leftMargin-xsmall,
39+
.formLabel--leftMargin-small {
40+
margin-right: var(--spacing-3);
41+
}
42+
43+
.formLabel--leftMargin-medium {
44+
margin-right: var(--spacing-4);
45+
}
46+
47+
.formLabel--leftMargin-large {
48+
margin-right: var(--spacing-5);
49+
}
50+
51+
/* Label margin bottom for top position */
52+
.formLabel--topMargin-xsmall,
53+
.formLabel--topMargin-small,
54+
.formLabel--topMargin-medium {
55+
margin-bottom: var(--spacing-2);
56+
}
57+
58+
.formLabel--topMargin-large {
59+
margin-bottom: var(--spacing-3);
60+
}
61+
62+
/* Label text container */
63+
.labelTextContainer {
64+
display: flex;
65+
flex-direction: row;
66+
align-items: center;
67+
gap: var(--spacing-2);
68+
max-height: 36px;
69+
}
70+
71+
/* Necessity indicator spacing */
72+
.labelTextContainer--optional {
73+
gap: var(--spacing-2);
74+
}
75+
76+
.labelTextContainer--required {
77+
gap: 0;
78+
}
79+
80+
/* Label suffix container */
81+
.labelSuffix {
82+
display: flex;
83+
align-items: center;
84+
margin-left: var(--spacing-2);
85+
}
86+
87+
/* Label trailing container */
88+
.labelTrailing {
89+
margin-left: auto;
90+
}
91+
92+
.labelTrailing--left {
93+
margin-left: 0;
94+
}
95+
96+
/* ============================================
97+
FORM HINT STYLES
98+
============================================ */
99+
100+
.formHint {
101+
display: flex;
102+
flex-direction: row;
103+
align-items: flex-start;
104+
gap: var(--spacing-2);
105+
}
106+
107+
/* Hint margin top based on size */
108+
.formHint--marginTop-xsmall,
109+
.formHint--marginTop-small,
110+
.formHint--marginTop-medium {
111+
margin-top: var(--spacing-2);
112+
}
113+
114+
.formHint--marginTop-large {
115+
margin-top: var(--spacing-3);
116+
}
117+
118+
/* Hint icon container */
119+
.hintIcon {
120+
display: flex;
121+
flex-shrink: 0;
122+
margin-top: var(--spacing-1);
123+
}
124+
125+
/* ============================================
126+
CHARACTER COUNTER STYLES
127+
============================================ */
128+
129+
.characterCounter {
130+
display: flex;
131+
align-items: center;
132+
}
133+
134+
.characterCounter--marginTop-xsmall,
135+
.characterCounter--marginTop-small,
136+
.characterCounter--marginTop-medium {
137+
margin-top: var(--spacing-2);
138+
}
139+
140+
.characterCounter--marginTop-large {
141+
margin-top: var(--spacing-3);
142+
}
143+
144+
.characterCounter--marginRight {
145+
margin-right: var(--spacing-1);
146+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
* Form Class Generators
3+
* Functions to generate CSS classes for Form components
4+
*/
5+
6+
// @ts-expect-error - CSS modules may not have type definitions in build
7+
import styles from './form.module.css';
8+
import type { FormSize, LabelPosition, NecessityIndicator } from './formTokens';
9+
10+
export type FormLabelOptions = {
11+
position: LabelPosition;
12+
size: FormSize;
13+
necessityIndicator?: NecessityIndicator;
14+
};
15+
16+
export type FormHintOptions = {
17+
size: FormSize;
18+
};
19+
20+
/**
21+
* Get CSS classes for form label
22+
*/
23+
export function getFormLabelClasses(options: FormLabelOptions): string {
24+
const { position, size } = options;
25+
26+
const classes = [
27+
styles.formLabel,
28+
styles[`formLabel--${position}`],
29+
];
30+
31+
if (position === 'left') {
32+
classes.push(styles[`formLabel--leftWidth-${size}`]);
33+
classes.push(styles[`formLabel--leftMargin-${size}`]);
34+
} else {
35+
classes.push(styles[`formLabel--topMargin-${size}`]);
36+
}
37+
38+
return classes.filter(Boolean).join(' ');
39+
}
40+
41+
/**
42+
* Get CSS classes for label text container
43+
*/
44+
export function getLabelTextContainerClasses(necessityIndicator: NecessityIndicator): string {
45+
const classes = [styles.labelTextContainer];
46+
47+
if (necessityIndicator === 'optional') {
48+
classes.push(styles['labelTextContainer--optional']);
49+
} else if (necessityIndicator === 'required') {
50+
classes.push(styles['labelTextContainer--required']);
51+
}
52+
53+
return classes.filter(Boolean).join(' ');
54+
}
55+
56+
/**
57+
* Get CSS classes for label suffix
58+
*/
59+
export function getLabelSuffixClasses(): string {
60+
return styles.labelSuffix;
61+
}
62+
63+
/**
64+
* Get CSS classes for label trailing
65+
*/
66+
export function getLabelTrailingClasses(isLabelLeft: boolean): string {
67+
const classes = [styles.labelTrailing];
68+
69+
if (isLabelLeft) {
70+
classes.push(styles['labelTrailing--left']);
71+
}
72+
73+
return classes.filter(Boolean).join(' ');
74+
}
75+
76+
/**
77+
* Get CSS classes for form hint
78+
*/
79+
export function getFormHintClasses(options: FormHintOptions): string {
80+
const { size } = options;
81+
82+
return `${styles.formHint} ${styles[`formHint--marginTop-${size}`]}`;
83+
}
84+
85+
/**
86+
* Get CSS classes for hint icon
87+
*/
88+
export function getHintIconClasses(): string {
89+
return styles.hintIcon;
90+
}
91+
92+
/**
93+
* Get CSS classes for character counter
94+
*/
95+
export function getCharacterCounterClasses(size: FormSize, hasMarginRight?: boolean): string {
96+
const classes = [
97+
styles.characterCounter,
98+
styles[`characterCounter--marginTop-${size}`],
99+
];
100+
101+
if (hasMarginRight) {
102+
classes.push(styles['characterCounter--marginRight']);
103+
}
104+
105+
return classes.filter(Boolean).join(' ');
106+
}
107+
108+
/**
109+
* Get template classes for use in Svelte components
110+
*/
111+
export function getFormTemplateClasses() {
112+
return {
113+
formLabel: styles.formLabel,
114+
labelTextContainer: styles.labelTextContainer,
115+
labelSuffix: styles.labelSuffix,
116+
labelTrailing: styles.labelTrailing,
117+
formHint: styles.formHint,
118+
hintIcon: styles.hintIcon,
119+
characterCounter: styles.characterCounter,
120+
};
121+
}
122+
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* Form Design Tokens
3+
* Matches packages/blade/src/components/Form/formTokens.ts
4+
*/
5+
6+
/**
7+
* Label text sizes based on position and input size
8+
*/
9+
export const labelTextSize = {
10+
top: {
11+
xsmall: 'small',
12+
small: 'small',
13+
medium: 'small',
14+
large: 'medium',
15+
},
16+
left: {
17+
xsmall: 'small',
18+
small: 'small',
19+
medium: 'medium',
20+
large: 'large',
21+
},
22+
} as const;
23+
24+
/**
25+
* Optional indicator text size
26+
*/
27+
export const labelOptionalIndicatorTextSize = {
28+
xsmall: 'small',
29+
small: 'small',
30+
medium: 'small',
31+
large: 'medium',
32+
} as const;
33+
34+
/**
35+
* Hint text sizes
36+
*/
37+
export const hintTextSize = {
38+
xsmall: 'small',
39+
small: 'small',
40+
medium: 'small',
41+
large: 'medium',
42+
} as const;
43+
44+
/**
45+
* Hint icon sizes
46+
*/
47+
export const hintIconSize = {
48+
xsmall: 'small',
49+
small: 'small',
50+
medium: 'small',
51+
large: 'medium',
52+
} as const;
53+
54+
/**
55+
* Hint margin top
56+
*/
57+
export const hintMarginTop = {
58+
xsmall: 'spacing.2',
59+
small: 'spacing.2',
60+
medium: 'spacing.2',
61+
large: 'spacing.3',
62+
} as const;
63+
64+
/**
65+
* Label margin bottom
66+
*/
67+
export const labelMarginBottom = {
68+
xsmall: 'spacing.2',
69+
small: 'spacing.2',
70+
medium: 'spacing.2',
71+
large: 'spacing.3',
72+
} as const;
73+
74+
/**
75+
* Label width for left-positioned labels
76+
*/
77+
export const labelWidth = {
78+
xsmall: 120,
79+
small: 120,
80+
medium: 120,
81+
large: 176,
82+
} as const;
83+
84+
/**
85+
* Label right margin for left-positioned labels
86+
*/
87+
export const labelLeftMarginRight = {
88+
xsmall: 'spacing.3',
89+
small: 'spacing.3',
90+
medium: 'spacing.4',
91+
large: 'spacing.5',
92+
} as const;
93+
94+
export type FormSize = 'xsmall' | 'small' | 'medium' | 'large';
95+
export type LabelPosition = 'top' | 'left';
96+
export type NecessityIndicator = 'required' | 'optional' | 'none';
97+
export type ValidationState = 'none' | 'error' | 'success';
98+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './formTokens';
2+
export * from './form';
3+

0 commit comments

Comments
 (0)