diff --git a/src/components/form-field/form-field/form-field.scss b/src/components/form-field/form-field/form-field.scss
index 8254ae2a0e..d145b8cd53 100644
--- a/src/components/form-field/form-field/form-field.scss
+++ b/src/components/form-field/form-field/form-field.scss
@@ -23,19 +23,28 @@
var(--sbb-icon-svg-width) + var(--sbb-form-field-gap)
);
--sbb-form-field-overflow: hidden;
+ --sbb-form-field-input-text-size: var(--sbb-font-size-text-m);
+ --sbb-form-field-label-text-size: var(--sbb-font-size-text-xs);
--sbb-form-field-label-size: calc(
- var(--sbb-font-size-text-xs) * var(--sbb-typo-line-height-body-text)
+ var(--sbb-form-field-label-text-size) * var(--sbb-typo-line-height-body-text)
);
- --sbb-form-field-input-size: calc(
- var(--sbb-font-size-text-m) * var(--sbb-typo-line-height-body-text)
+ --sbb-form-field-text-size: calc(
+ var(--sbb-form-field-input-text-size) * var(--sbb-typo-line-height-body-text)
);
+
+ // fixme +2px to not cut low underscore letters
+ --sbb-form-field-input-size: calc(var(--sbb-form-field-input-text-size) + 2px);
--sbb-form-field-margin-block-start: calc(
(
var(--sbb-form-field-min-height) - var(--sbb-form-field-label-size) - var(
- --sbb-form-field-input-size
+ --sbb-form-field-text-size
) + var(--sbb-form-field-label-to-input-overlapping)
) / 2
);
+ --sbb-form-field-spacer-margin-block-end: calc(
+ -1 * var(--sbb-form-field-label-to-input-overlapping)
+ );
+ --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(8.5)};
// Lock sbb-icon size
--sbb-icon-svg-width: var(--sbb-size-icon-ui-small);
@@ -45,6 +54,10 @@
// to default color for cases where the form field is used in a negative context.
--sbb-focus-outline-color: var(--sbb-focus-outline-color-default);
+ @include sbb.mq($from: medium) {
+ --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(10.5)};
+ }
+
@include sbb.if-forced-colors {
--sbb-form-field-border-color: ButtonBorder;
}
@@ -76,6 +89,22 @@
--sbb-form-field-padding-inline: var(--sbb-spacing-responsive-xxxs);
}
+:host([size='s']) {
+ --sbb-form-field-min-height: var(--sbb-size-element-xs);
+ --sbb-form-field-padding-inline: var(--sbb-spacing-fixed-2x);
+ --sbb-form-field-input-text-size: var(--sbb-font-size-text-s);
+ --sbb-form-field-label-text-size: var(--sbb-font-size-text-xxs);
+ --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(10)};
+ --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-11)};
+ --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(5.125)};
+
+ @include sbb.mq($from: medium) {
+ --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(11)};
+ --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-8)};
+ --sbb-form-field-floating-label-transform: #{sbb.px-to-rem-build(6.5)};
+ }
+}
+
:host([error-space='reserve']) {
--sbb-form-field-error-min-height: calc(
var(--sbb-typo-line-height-body-text) * var(--sbb-font-size-text-xs)
@@ -174,6 +203,15 @@
// Should be after other definitions to override overflow
:host([data-input-type='sbb-slider']) {
--sbb-form-field-overflow: visible;
+ --sbb-form-field-min-height: #{sbb.px-to-rem-build(48)};
+ --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(0.5)};
+ --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-6)};
+
+ @include sbb.mq($from: medium) {
+ --sbb-form-field-min-height: #{sbb.px-to-rem-build(52)};
+ --sbb-form-field-label-to-input-overlapping: #{sbb.px-to-rem-build(1)};
+ --sbb-form-field-spacer-margin-block-end: #{sbb.px-to-rem-build(-3)};
+ }
}
:host([data-input-type='textarea']) {
@@ -284,10 +322,10 @@
.sbb-form-field__label-spacer {
display: flex;
- height: calc(var(--sbb-font-size-text-xs) * var(--sbb-typo-line-height-body-text));
+ height: calc(var(--sbb-form-field-label-text-size) * var(--sbb-typo-line-height-body-text));
// Moves label down and input up to meet positioning requirements
- margin-block-end: calc(-1 * var(--sbb-form-field-label-to-input-overlapping));
+ margin-block-end: var(--sbb-form-field-spacer-margin-block-end);
}
// To avoid doubled payload, we group the rules.
@@ -311,6 +349,10 @@
inset-block-start: 0;
color: var(--sbb-form-field-label-color);
+ :host([size='s']) & {
+ @include sbb.text-xxs--regular;
+ }
+
:host([data-input-type='select']) &,
:host([data-input-type='sbb-select']) & {
padding-inline-end: var(--sbb-form-field-select-inline-padding-end);
@@ -340,12 +382,8 @@
)[data-input-empty]
)
& {
- font-size: var(--sbb-font-size-text-m);
- transform: translateY(#{sbb.px-to-rem-build(8.5)});
-
- @include sbb.mq($from: medium) {
- transform: translateY(#{sbb.px-to-rem-build(10.5)});
- }
+ font-size: var(--sbb-form-field-input-text-size);
+ transform: translateY(var(--sbb-form-field-floating-label-transform));
}
}
@@ -353,6 +391,11 @@
@include sbb.ellipsis;
}
+.sbb-form-field__input {
+ // TODO: find better solution
+ height: 0;
+}
+
// Input
.sbb-form-field__input ::slotted(:where(input, select, textarea, sbb-select)) {
@@ -375,9 +418,10 @@
// To be more specific than the styles in normalize.scss we need to use !important
// TODO: Find a better solution
- font-size: var(--sbb-font-size-text-m) !important;
+ font-size: var(--sbb-form-field-input-text-size) !important;
font-family: var(--sbb-typo-font-family) !important;
line-height: var(--sbb-typo-line-height-body-text) !important;
+ height: var(--sbb-form-field-text-size);
&::placeholder {
@include sbb.placeholder;
diff --git a/src/components/form-field/form-field/form-field.stories.ts b/src/components/form-field/form-field/form-field.stories.ts
index f554458b04..460063c491 100644
--- a/src/components/form-field/form-field/form-field.stories.ts
+++ b/src/components/form-field/form-field/form-field.stories.ts
@@ -409,7 +409,7 @@ const size: InputType = {
control: {
type: 'inline-radio',
},
- options: ['m', 'l'],
+ options: ['s', 'm', 'l'],
table: {
category: 'Form-field',
},
@@ -459,7 +459,7 @@ const basicArgs: Args = {
'floating-label': false,
optional: false,
borderless: false,
- size: size.options![0],
+ size: size.options![1],
negative: false,
cssClass: '',
placeholder: 'Input placeholder',
@@ -483,10 +483,16 @@ export const InputSizeL: StoryObj = {
args: {
...basicArgs,
value: 'This input value is so long that it needs ellipsis to fit.',
- size: 'l',
+ size: size.options![2],
},
};
+export const InputSizeS: StoryObj = {
+ render: TemplateInput,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, size: size.options![0] },
+};
+
export const InputNoLabel: StoryObj = {
render: TemplateInput,
argTypes: basicArgTypes,
@@ -529,6 +535,18 @@ export const InputOptionalAndIcons: StoryObj = {
args: { ...basicArgs, optional: true },
};
+export const InputOptionalAndIconsSizeS: StoryObj = {
+ render: TemplateInputWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, size: size.options![0] },
+};
+
+export const InputOptionalAndIconsSizeL: StoryObj = {
+ render: TemplateInputWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, size: size.options![2] },
+};
+
export const InputWithMiniButton: StoryObj = {
render: TemplateInputWithMiniButton,
argTypes: basicArgTypes,
@@ -640,6 +658,18 @@ export const SelectOptionalAndIcons: StoryObj = {
args: { ...basicArgs, optional: true },
};
+export const SelectOptionalAndIconsSizeS: StoryObj = {
+ render: TemplateSelectWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, size: size.options![0] },
+};
+
+export const SelectOptionalAndIconsSizeL: StoryObj = {
+ render: TemplateSelectWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, size: size.options![2] },
+};
+
export const Textarea: StoryObj = {
render: TemplateTextarea,
argTypes: basicArgTypes,
@@ -676,6 +706,18 @@ export const TextareaOptionalAndIcon: StoryObj = {
args: { ...basicArgs, optional: true },
};
+export const TextareaOptionalAndIconSizeS: StoryObj = {
+ render: TemplateTextareaWithIcon,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, size: size.options![0] },
+};
+
+export const TextareaOptionalAndIconSizeL: StoryObj = {
+ render: TemplateTextareaWithIcon,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, size: size.options![2] },
+};
+
export const TextareaFloatingLabel: StoryObj = {
render: TemplateTextarea,
argTypes: basicArgTypes,
@@ -755,6 +797,18 @@ export const InputOptionalAndIconsNegative: StoryObj = {
args: { ...basicArgs, optional: true, negative: true },
};
+export const InputOptionalAndIconsNegativeSizeS: StoryObj = {
+ render: TemplateInputWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, negative: true, size: size.options![0] },
+};
+
+export const InputOptionalAndIconsNegativeSizeL: StoryObj = {
+ render: TemplateInputWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, negative: true, size: size.options![2] },
+};
+
export const InputWithMiniButtonNegative: StoryObj = {
render: TemplateInputWithMiniButton,
argTypes: basicArgTypes,
@@ -869,6 +923,18 @@ export const SelectOptionalAndIconsNegative: StoryObj = {
args: { ...basicArgs, optional: true, negative: true },
};
+export const SelectOptionalAndIconsNegativeSizeS: StoryObj = {
+ render: TemplateSelectWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, negative: true, size: size.options![0] },
+};
+
+export const SelectOptionalAndIconsNegativeSizeL: StoryObj = {
+ render: TemplateSelectWithIcons,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, negative: true, size: size.options![2] },
+};
+
export const InputCollapsedWidthNegative: StoryObj = {
render: TemplateInput,
argTypes: basicArgTypes,
@@ -917,6 +983,18 @@ export const TextareaOptionalAndIconNegative: StoryObj = {
args: { ...basicArgs, optional: true, negative: true },
};
+export const TextareaOptionalAndIconNegativeSizeS: StoryObj = {
+ render: TemplateTextareaWithIcon,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, negative: true, size: size.options![0] },
+};
+
+export const TextareaOptionalAndIconNegativeSizeL: StoryObj = {
+ render: TemplateTextareaWithIcon,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, optional: true, negative: true, size: size.options![2] },
+};
+
export const TextareaFloatingLabelNegative: StoryObj = {
render: TemplateTextarea,
argTypes: basicArgTypes,
diff --git a/src/components/form-field/form-field/form-field.ts b/src/components/form-field/form-field/form-field.ts
index 7c7dd0acaa..1ddc243cf3 100644
--- a/src/components/form-field/form-field/form-field.ts
+++ b/src/components/form-field/form-field/form-field.ts
@@ -75,7 +75,7 @@ export class SbbFormFieldElement extends SbbNegativeMixin(LitElement) {
@property({ type: Boolean }) public optional?: boolean;
/** Size variant, either l or m. */
- @property({ reflect: true }) public size?: 'l' | 'm' = 'm';
+ @property({ reflect: true }) public size?: 'l' | 'm' | 's' = 'm';
/** Whether to display the form field without a border. */
@property({ reflect: true, type: Boolean }) public borderless = false;
diff --git a/src/components/form-field/form-field/readme.md b/src/components/form-field/form-field/readme.md
index 6f4780c08f..e6237b7ce8 100644
--- a/src/components/form-field/form-field/readme.md
+++ b/src/components/form-field/form-field/readme.md
@@ -94,6 +94,21 @@ Please refer to their documentation for more details.
## Style
+The component has a `size` property, which accepts three different values: `s`, `m` (default) and `l`.
+
+```html
+
+
+
+
+
+
+
+
+ This field is required!
+
+```
+
By default, the component has a defined width and min-width. However, this behavior can be overridden by setting
the `width` property to `collapse`: in this way the component adapts its width to the inner slotted input component.
This is useful, for example, for the [sbb-time-input](/docs/components-sbb-time-input--docs) component.
@@ -142,7 +157,7 @@ technology will announce errors when they appear.
| `inputElement` | - | public | `HTMLInputElement \| HTMLSelectElement \| HTMLElement \| undefined` | | Returns the input element. |
| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. |
| `optional` | `optional` | public | `boolean \| undefined` | | Indicates whether the input is optional. |
-| `size` | `size` | public | `'l' \| 'm' \| undefined` | `'m'` | Size variant, either l or m. |
+| `size` | `size` | public | `'l' \| 'm' \| 's' \| undefined` | `'m'` | Size variant, either l or m. |
| `width` | `width` | public | `'default' \| 'collapse'` | `'default'` | Defines the width of the component: - `default`: the component has defined width and min-width; - `collapse`: the component adapts itself to its inner input content. |
## Methods
diff --git a/src/components/slider/slider.stories.ts b/src/components/slider/slider.stories.ts
index f349057608..d04b75e8d6 100644
--- a/src/components/slider/slider.stories.ts
+++ b/src/components/slider/slider.stories.ts
@@ -40,13 +40,18 @@ const TemplateSlottedIcons = (args: Args): TemplateResult => html`
`;
-const TemplateSbbSliderInFormField = ({ label, optional, ...args }: Args): TemplateResult => html`
-
+const TemplateSbbSliderInFormField = ({
+ label,
+ optional,
+ size,
+ ...args
+}: Args): TemplateResult => html`
+
${label ? html`` : nothing} ${TemplateSbbSlider(args)}
`;
-const valueArg: InputType = {
+const value: InputType = {
control: {
type: 'text',
},
@@ -55,7 +60,7 @@ const valueArg: InputType = {
},
};
-const valueAsNumberArg: InputType = {
+const valueAsNumber: InputType = {
control: {
type: 'number',
},
@@ -64,7 +69,7 @@ const valueAsNumberArg: InputType = {
},
};
-const minArg: InputType = {
+const min: InputType = {
control: {
type: 'text',
},
@@ -73,7 +78,7 @@ const minArg: InputType = {
},
};
-const maxArg: InputType = {
+const max: InputType = {
control: {
type: 'text',
},
@@ -82,7 +87,7 @@ const maxArg: InputType = {
},
};
-const disabledArg: InputType = {
+const disabled: InputType = {
control: {
type: 'boolean',
},
@@ -91,7 +96,7 @@ const disabledArg: InputType = {
},
};
-const readonlyArg: InputType = {
+const readonly: InputType = {
control: {
type: 'boolean',
},
@@ -100,7 +105,7 @@ const readonlyArg: InputType = {
},
};
-const startIconArg: InputType = {
+const startIcon: InputType = {
control: {
type: 'text',
},
@@ -109,7 +114,7 @@ const startIconArg: InputType = {
},
};
-const endIconArg: InputType = {
+const endIcon: InputType = {
control: {
type: 'text',
},
@@ -127,7 +132,7 @@ const ariaLabel: InputType = {
},
};
-const labelArgArg: InputType = {
+const label: InputType = {
control: {
type: 'text',
},
@@ -136,7 +141,7 @@ const labelArgArg: InputType = {
},
};
-const optionalArg: InputType = {
+const optional: InputType = {
control: {
type: 'boolean',
},
@@ -145,22 +150,33 @@ const optionalArg: InputType = {
},
};
+const size: InputType = {
+ control: {
+ type: 'inline-radio',
+ },
+ options: ['s', 'm', 'l'],
+ table: {
+ category: 'Form-field attribute',
+ },
+};
+
const basicArgTypes: ArgTypes = {
- max: maxArg,
- min: minArg,
- disabled: disabledArg,
- readonly: readonlyArg,
- value: valueArg,
- 'value-as-number': valueAsNumberArg,
- 'start-icon': startIconArg,
- 'end-icon': endIconArg,
+ max,
+ min,
+ disabled,
+ readonly,
+ value,
+ 'value-as-number': valueAsNumber,
+ 'start-icon': startIcon,
+ 'end-icon': endIcon,
'aria-label': ariaLabel,
};
const formFieldBasicArgsTypes: ArgTypes = {
...basicArgTypes,
- label: labelArgArg,
- optional: optionalArg,
+ label,
+ optional,
+ size,
};
const basicArgs: Args = {
@@ -179,6 +195,7 @@ const formFieldBasicArgs = {
...basicArgs,
label: 'Label',
optional: undefined,
+ size: size.options![1],
};
export const sbbSlider: StoryObj = {
@@ -239,6 +256,12 @@ export const sbbSliderInFormFieldDisabled: StoryObj = {
args: { ...formFieldBasicArgs, disabled: true },
};
+export const sbbSliderInFormFieldSizeS: StoryObj = {
+ render: TemplateSbbSliderInFormField,
+ argTypes: { ...formFieldBasicArgsTypes },
+ args: { ...formFieldBasicArgs, size: size.options![0] },
+};
+
const meta: Meta = {
decorators: [withActions as Decorator],
parameters: {