-
-
Notifications
You must be signed in to change notification settings - Fork 459
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(experience): add label for input field (#6200)
- Loading branch information
Showing
16 changed files
with
383 additions
and
96 deletions.
There are no files selected for viewing
127 changes: 127 additions & 0 deletions
127
packages/experience/src/components/InputFields/InputField/NotchedBorder/index.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
@use '@/scss/underscore' as _; | ||
|
||
.container { | ||
position: absolute; | ||
inset: 0; | ||
pointer-events: none; | ||
|
||
.border, | ||
.outline { | ||
text-align: left; | ||
position: absolute; | ||
inset: -8px 0 0; | ||
border: _.border(var(--color-line-border)); | ||
border-radius: var(--radius); | ||
pointer-events: none; | ||
display: block; | ||
|
||
legend { | ||
display: inline-block; | ||
visibility: hidden; | ||
padding: 0; | ||
// fix to the label font height to keep space for the input container | ||
height: 20px; | ||
// Set to 0 to avoid the empty space in the top border | ||
width: 0; | ||
|
||
span { | ||
padding: 0 _.unit(1); | ||
opacity: 0%; | ||
visibility: hidden; | ||
display: inline-block; | ||
font: var(--font-body-3); | ||
} | ||
} | ||
} | ||
|
||
.outline { | ||
display: none; | ||
inset: -9px -2px -2px; | ||
border-radius: 10px; | ||
border: 3px outset var(--color-overlay-brand-focused); | ||
} | ||
|
||
.label { | ||
position: absolute; | ||
left: _.unit(4); | ||
font: var(--font-body-1); | ||
color: var(--color-type-secondary); | ||
pointer-events: none; | ||
top: 50%; | ||
transform: translateY(-46%); | ||
transition: 0.2s ease-out; | ||
transition-property: position, font, top; | ||
background-color: transparent; | ||
z-index: 1; | ||
} | ||
|
||
&.active { | ||
.border, | ||
.outline { | ||
legend { | ||
visibility: visible; | ||
width: auto; | ||
} | ||
} | ||
|
||
.label { | ||
top: 0; | ||
left: _.unit(3); | ||
font: var(--font-body-3); | ||
color: var(--color-type-secondary); | ||
padding: 0 _.unit(1); | ||
} | ||
} | ||
|
||
&.focused { | ||
.border { | ||
border-color: var(--color-brand-default); | ||
} | ||
|
||
.label { | ||
color: var(--color-brand-default); | ||
} | ||
} | ||
} | ||
|
||
|
||
.container.danger { | ||
.border { | ||
border-color: var(--color-danger-default); | ||
} | ||
|
||
.outline { | ||
border-color: var(--color-overlay-danger-focused); | ||
} | ||
|
||
&.active { | ||
.label { | ||
color: var(--color-danger-default); | ||
} | ||
} | ||
} | ||
|
||
.container.noLabel { | ||
.border, | ||
.outline { | ||
legend { | ||
width: 0; | ||
} | ||
} | ||
} | ||
|
||
:global(body.desktop) { | ||
.container { | ||
&:not(.active) { | ||
.label { | ||
font: var(--font-body-2); | ||
} | ||
} | ||
|
||
&.focused { | ||
.outline { | ||
display: block; | ||
} | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
packages/experience/src/components/InputFields/InputField/NotchedBorder/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import classNames from 'classnames'; | ||
|
||
import * as styles from './index.module.scss'; | ||
|
||
type Props = { | ||
readonly label: string; | ||
readonly isActive: boolean; | ||
readonly isDanger: boolean; | ||
readonly isFocused: boolean; | ||
}; | ||
|
||
/** | ||
* NotchedBorder Component | ||
* | ||
* This component creates a customizable border with a notch for labels in form inputs. | ||
* It enhances the visual appearance and accessibility of input fields by providing | ||
* a floating label effect and customizable border/outline styles. | ||
* | ||
* Key implementation details: | ||
* 1. Uses two fieldset elements: | ||
* - The first creates the main border effect. | ||
* - The second creates a separate outline effect, avoiding gaps between the built-in outline and border. | ||
* 2. Utilizes the fieldset's legend for the label, allowing it to overlap the border: | ||
* - This creates a "floating" label effect. | ||
* - The legend's background becomes transparent, preventing it from blocking the background. | ||
* | ||
*/ | ||
const NotchedBorder = ({ label, isActive, isDanger, isFocused }: Props) => { | ||
return ( | ||
<div | ||
className={classNames( | ||
styles.container, | ||
isDanger && styles.danger, | ||
isActive && styles.active, | ||
isFocused && styles.focused, | ||
!label && styles.noLabel | ||
)} | ||
> | ||
<fieldset className={styles.border}> | ||
<legend> | ||
<span>{label}</span> | ||
</legend> | ||
</fieldset> | ||
<fieldset className={styles.outline}> | ||
<legend> | ||
<span>{label}</span> | ||
</legend> | ||
</fieldset> | ||
{Boolean(label) && <label className={styles.label}>{label}</label>} | ||
</div> | ||
); | ||
}; | ||
|
||
export default NotchedBorder; |
139 changes: 76 additions & 63 deletions
139
packages/experience/src/components/InputFields/InputField/index.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,106 @@ | ||
@use '@/scss/underscore' as _; | ||
|
||
.inputField { | ||
.container { | ||
position: relative; | ||
@include _.flex-row; | ||
border: _.border(var(--color-line-border)); | ||
border-radius: var(--radius); | ||
overflow: hidden; | ||
transition-property: outline, border; | ||
transition-timing-function: ease-in-out; | ||
transition-duration: 0.2s; | ||
align-items: stretch; | ||
|
||
// fix in safari input field line-height issue | ||
height: 44px; | ||
|
||
input { | ||
transition: width 0.3s ease-in; | ||
padding: 0 _.unit(4); | ||
flex: 1; | ||
background: var(--color-bg-body); | ||
caret-color: var(--color-brand-default); | ||
font: var(--font-body-1); | ||
color: var(--color-type-primary); | ||
.inputField { | ||
position: relative; | ||
@include _.flex-row; | ||
overflow: hidden; | ||
transition-property: border; | ||
transition-timing-function: ease-in-out; | ||
transition-duration: 0.2s; | ||
background: inherit; | ||
align-items: stretch; | ||
|
||
// fix in safari input field line-height issue | ||
height: 44px; | ||
|
||
&::placeholder { | ||
color: var(--color-type-secondary); | ||
input { | ||
transition: width 0.3s ease-in; | ||
padding: 0 _.unit(4); | ||
flex: 1; | ||
background: inherit; | ||
caret-color: var(--color-brand-default); | ||
font: var(--font-body-1); | ||
color: var(--color-type-primary); | ||
outline: none; | ||
border-radius: var(--radius); | ||
margin: -1px 1px 0; | ||
|
||
&::placeholder { | ||
color: var(--color-type-secondary); | ||
transition: opacity 0.2s ease-out; | ||
opacity: 0%; | ||
} | ||
} | ||
} | ||
|
||
.suffix { | ||
position: absolute; | ||
right: _.unit(2); | ||
top: 50%; | ||
transform: translateY(-50%); | ||
width: _.unit(8); | ||
height: _.unit(8); | ||
display: none; | ||
z-index: 1; | ||
} | ||
|
||
.suffix { | ||
position: absolute; | ||
right: _.unit(2); | ||
top: 50%; | ||
transform: translateY(-50%); | ||
width: _.unit(8); | ||
height: _.unit(8); | ||
display: none; | ||
z-index: 1; | ||
} | ||
|
||
&:focus-within { | ||
border: _.border(var(--color-brand-default)); | ||
&.isSuffixFocusVisible:focus-within { | ||
input { | ||
padding-right: _.unit(10); | ||
} | ||
|
||
input { | ||
outline: none; | ||
.suffix { | ||
display: flex; | ||
} | ||
} | ||
} | ||
|
||
&.danger { | ||
border: _.border(var(--color-danger-default)); | ||
|
||
input { | ||
caret-color: var(--color-danger-default); | ||
&.active { | ||
.inputField { | ||
input::placeholder { | ||
opacity: 100%; | ||
} | ||
} | ||
} | ||
|
||
&.isSuffixFocusVisible:focus-within { | ||
input { | ||
padding-right: _.unit(10); | ||
&.noLabel { | ||
.inputField { | ||
input::placeholder { | ||
opacity: 100%; | ||
} | ||
} | ||
} | ||
|
||
.suffix { | ||
display: flex; | ||
&.danger { | ||
.inputField { | ||
/* stylelint-disable-next-line no-descending-specificity */ | ||
input { | ||
caret-color: var(--color-danger-default); | ||
} | ||
} | ||
} | ||
|
||
// override for firefox & safari focus outline since we are using custom notchedOutline | ||
&:focus-visible { | ||
outline: none; | ||
} | ||
} | ||
|
||
.errorMessage { | ||
margin-top: _.unit(1); | ||
margin-left: _.unit(0.5); | ||
} | ||
|
||
:global(body.desktop) { | ||
.inputField { | ||
outline: 3px solid transparent; | ||
|
||
/* stylelint-disable-next-line no-descending-specificity */ | ||
input { | ||
font: var(--font-body-2); | ||
background: var(--color-bg-float); | ||
} | ||
|
||
&:focus-within { | ||
outline-color: var(--color-overlay-brand-focused); | ||
} | ||
|
||
&.danger:focus-within { | ||
outline-color: var(--color-overlay-danger-focused); | ||
:global(body.desktop) { | ||
.container { | ||
.inputField { | ||
/* stylelint-disable-next-line no-descending-specificity */ | ||
input { | ||
font: var(--font-body-2); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.