@ -1,129 +1,168 @@
/* PasswordField — single-line password input with visibility toggle. */
/ *
* PasswordField — single-line password input with visibility toggle ,
* CapsLock indicator and optional strength meter .
*
* Mirrors search-field ' s chrome ( border , radius , focus ring , padding ,
* disabled opacity ) so every text input in the system reads the same
* — only the affordances inside the control differ .
* /
[ data-password-field ] {
[ data-password-field ] {
--_password-field-px : var ( --space-3 ) ;
--_password-field-height : var ( --password-field-height-md ) ;
--_password-field-py : var ( --space-2 ) ;
--_password-field-px : var ( --password-field-px-md ) ;
--_password-field-gap : var ( --space-2 ) ;
--_password-field-gap : var ( --password-field-gap-md ) ;
--_password-field-font-size : var ( --font-size-md ) ;
--_password-field-font-size : var ( --password-field-font-size-md ) ;
--_password-field-line-height : var ( --leading-normal ) ;
--_password-field-trigger-size : var ( --password-field-icon-size-md ) ;
--_password-field-radius : var ( --radius-md ) ;
--_password-field-caps-font-size : var ( --password-field-caps-font-size-md ) ;
--_password-field-border : var ( --color-neutral-border ) ;
--_password-field-accent-border : var ( --color-primary-border ) ;
--_password-field-border-hover : var ( --color-neutral-border-strong ) ;
--_password-field-accent-track : var ( --color-primary-track ) ;
--_password-field-border-focus : var ( --color-primary-border-strong ) ;
--_password-field-accent-text : var ( --color-primary-text ) ;
--_password-field-bg : var ( --color-surface ) ;
--_password-field-color : var ( --color-content ) ;
--_password-field-muted : var ( --color-content-muted ) ;
display : inline-flex ;
display : inline-flex ;
flex-wrap : wrap ;
flex-wrap : wrap ;
align-items : stretch ;
align-items : center ;
gap : var ( --_password-field-gap ) ;
gap : var ( --_password-field-gap ) ;
inline-size : 100 % ;
inline-size : 100 % ;
min-inline-size : 0 ;
min-inline-size : 0 ;
position : relative ;
block-size : var ( --_password-field-height ) ;
padding-inline : var ( --_password-field-px ) ;
border : var ( --password-field-border-width ) solid var ( --password-field-border ) ;
border-radius : var ( --password-field-radius ) ;
background : var ( --password-field-bg ) ;
color : var ( --password-field-color ) ;
font-family : var ( --password-field-font-family ) ;
font-size : var ( --_password-field-font-size ) ;
line-height : var ( --password-field-line-height ) ;
transition :
background var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ,
border-color var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ,
box-shadow var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ;
}
}
[ data-password-field ] [ data-size = 'xs' ] {
/ * When the StrengthMeter is composed , it claims a second row and the
--_password-field-px : var ( --space-2 ) ;
* height auto-grows ; otherwise the single-row chrome rules . * /
--_password-field-py : var ( --space-1 ) ;
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) {
--_password-field-font-size : var ( --font-size-xs ) ;
block-size : auto ;
--_password-field-radius : var ( --radius-sm ) ;
min-block-size : var ( --_password-field-height ) ;
}
padding-block : var ( --password-field-gap-sm ) ;
[ data-password-field ] [ data-size = 'sm' ] {
--_password-field-px : var ( --space-2 ) ;
--_password-field-py : var ( --space-1-5 ) ;
--_password-field-font-size : var ( --font-size-sm ) ;
}
[ data-password-field ] [ data-size = 'lg' ] {
--_password-field-px : var ( --space-4 ) ;
--_password-field-py : var ( --space-3 ) ;
--_password-field-font-size : var ( --font-size-lg ) ;
--_password-field-radius : var ( --radius-lg ) ;
}
[ data-password-field ] [ data-size = 'xl' ] {
--_password-field-px : var ( --space-5 ) ;
--_password-field-py : var ( --space-4 ) ;
--_password-field-font-size : var ( --font-size-xl ) ;
--_password-field-radius : var ( --radius-lg ) ;
}
}
/* Color accents on focus + invalid. */
/* ── Color accents ─────────────────────────────────────────────────── */
[ data-password-field ] [ data-color = 'secondary' ] {
[ data-password-field ] [ data-color = 'secondary' ] {
--_password-field-border-focus : var ( --color-secondary-border-strong ) ;
--_password-field-accent-border : var ( --color-secondary-border ) ;
--_password-field-accent-track : var ( --color-secondary-track ) ;
--_password-field-accent-text : var ( --color-secondary-text ) ;
}
}
[ data-password-field ] [ data-color = 'neutral' ] {
[ data-password-field ] [ data-color = 'neutral' ] {
--_password-field-border-focus : var ( --color-neutral-border-strong ) ;
--_password-field-accent-border : var ( --color-neutral-border ) ;
--_password-field-accent-track : var ( --color-neutral-track ) ;
--_password-field-accent-text : var ( --color-neutral-text ) ;
}
}
[ data-password-field ] [ data-color = 'affirm' ] {
[ data-password-field ] [ data-color = 'affirm' ] {
--_password-field-border-focus : var ( --color-affirm-border-strong ) ;
--_password-field-accent-border : var ( --color-affirm-border ) ;
--_password-field-accent-track : var ( --color-affirm-track ) ;
--_password-field-accent-text : var ( --color-affirm-text ) ;
}
}
[ data-password-field ] [ data-color = 'fulfill' ] {
[ data-password-field ] [ data-color = 'fulfill' ] {
--_password-field-border-focus : var ( --color-fulfill-border-strong ) ;
--_password-field-accent-border : var ( --color-fulfill-border ) ;
--_password-field-accent-track : var ( --color-fulfill-track ) ;
--_password-field-accent-text : var ( --color-fulfill-text ) ;
}
}
[ data-password-field ] [ data-color = 'risk' ] {
[ data-password-field ] [ data-color = 'risk' ] {
--_password-field-border-focus : var ( --color-risk-border-strong ) ;
--_password-field-accent-border : var ( --color-risk-border ) ;
--_password-field-accent-track : var ( --color-risk-track ) ;
--_password-field-accent-text : var ( --color-risk-text ) ;
}
}
[ data-password-field ] [ data-color = 'threat' ] {
[ data-password-field ] [ data-color = 'threat' ] {
--_password-field-border-focus : var ( --color-threat-border-strong ) ;
--_password-field-accent-border : var ( --color-threat-border ) ;
--_password-field-accent-track : var ( --color-threat-track ) ;
--_password-field-accent-text : var ( --color-threat-text ) ;
}
}
[ data-password-field ] [ data-color = 'loss' ] {
[ data-password-field ] [ data-color = 'loss' ] {
--_password-field-border-focus : var ( --color-loss-border-strong ) ;
--_password-field-accent-border : var ( --color-loss-border ) ;
--_password-field-accent-track : var ( --color-loss-track ) ;
--_password-field-accent-text : var ( --color-loss-text ) ;
}
}
/* ── Control row (input + trigger + caps indicator) ───────────────── */
/* ── Sizes ────────────────────────────────────────── ───────────────── */
[ data-password-field ] {
[ data-password-field ] [ data-size = 'xs' ] {
border : var ( --border-width ) solid var ( --_password-field-border ) ;
--_password-field-height : var ( --password-field-height-xs ) ;
border-radius : var ( --_password-field-radius ) ;
--_password-field-px : var ( --password-field-px-xs ) ;
background : var ( --_password-field-bg ) ;
--_password-field-gap : var ( --password-field-gap-xs ) ;
padding-inline : var ( --_password-field-px ) ;
--_password-field-font-size : var ( --password-field-font-size-xs ) ;
transition :
--_password-field-trigger-size : var ( --password-field-icon-size-xs ) ;
border-color var ( --duration-fast ) var ( --ease-default ) ,
--_password-field-caps-font-size : var ( --password-field-caps-font-size-xs ) ;
box-shadow var ( --duration-fast ) var ( --ease-default ) ;
}
[ data-password-field ] [ data-size = 'sm' ] {
--_password-field-height : var ( --password-field-height-sm ) ;
--_password-field-px : var ( --password-field-px-sm ) ;
--_password-field-gap : var ( --password-field-gap-sm ) ;
--_password-field-font-size : var ( --password-field-font-size-sm ) ;
--_password-field-trigger-size : var ( --password-field-icon-size-sm ) ;
--_password-field-caps-font-size : var ( --password-field-caps-font-size-sm ) ;
}
[ data-password-field ] [ data-size = 'lg' ] {
--_password-field-height : var ( --password-field-height-lg ) ;
--_password-field-px : var ( --password-field-px-lg ) ;
--_password-field-gap : var ( --password-field-gap-lg ) ;
--_password-field-font-size : var ( --password-field-font-size-lg ) ;
--_password-field-trigger-size : var ( --password-field-icon-size-lg ) ;
--_password-field-caps-font-size : var ( --password-field-caps-font-size-lg ) ;
}
[ data-password-field ] [ data-size = 'xl' ] {
--_password-field-height : var ( --password-field-height-xl ) ;
--_password-field-px : var ( --password-field-px-xl ) ;
--_password-field-gap : var ( --password-field-gap-xl ) ;
--_password-field-font-size : var ( --password-field-font-size-xl ) ;
--_password-field-trigger-size : var ( --password-field-icon-size-xl ) ;
--_password-field-caps-font-size : var ( --password-field-caps-font-size-xl ) ;
}
}
/* ── Variants ──────────────────────────────────────────────────────── */
[ data-password-field ] [ data-variant = 'outline' ] {
[ data-password-field ] [ data-variant = 'outline' ] {
background : transparent ;
background : transparent ;
}
}
[ data-password-field ] [ data-variant = 'ghost' ] {
[ data-password-field ] [ data-variant = 'ghost' ] {
background : transparent ;
border-color : transparent ;
border-color : transparent ;
background : transparent ;
padding-inline : 0 ;
}
}
[ data-password-field ] : hover : not ( [ data-disabled ] ) : not ( [ data-readonly ] ) {
/* ── State chrome ──────────────────────────────────────────────────── */
border-color : var ( --_password-field-border-hover ) ;
}
[ data-password-field ] [ data-focused ] {
[ data-password-field ] [ data-focused ] {
border-color : var ( --_password-field-border-focus ) ;
border-color : var ( --password-field-border-focus ) ;
box-shadow : 0 0 0 var ( --ring-width , 2px )
box-shadow : var ( --password-field-focus-shadow ) ;
color-mix ( in srgb , var ( --_password-field-border-focus ) 25 % , transparent ) ;
}
}
[ data-password-field ] [ data-invalid ] {
[ data-password-field ] [ data-invalid ] {
border-color : var ( --color-threat-border-strong ) ;
border-color : var ( -- password-field-border-invalid ) ;
}
}
[ data-password-field ] [ data-disabled ] {
[ data-password-field ] [ data-disabled ] {
opacity : var ( -- opacity-disabled, 0 . 6 ) ;
opacity : var ( -- password-field-disabled-opacity ) ;
}
}
/* ── Input ─────────────────────────────────────────────────────────── */
[ data-password-field-input ] {
[ data-password-field-input ] {
flex : 1 1 auto ;
flex : 1 1 auto ;
min-inline-size : 0 ;
min-inline-size : 0 ;
padding-block : var ( --_password-field-py ) ;
border : 0 ;
border : 0 ;
background : transparent ;
background : transparent ;
color : var ( --_password-field-color ) ;
color : inherit ;
font-family : var ( --font-family-body ) ;
font : inherit ;
font-size : var ( --_password-field-font-size ) ;
line-height : var ( --_password-field-line-height ) ;
outline : none ;
outline : none ;
letter-spacing : 0 . 05em ;
/* Slightly wider tracking helps recognise the · placeholder mask. */
letter-spacing : 0 . 02em ;
}
}
[ data-password-field-input ] :: placeholder {
[ data-password-field-input ] :: placeholder {
color : var ( -- _password-field-muted ) ;
color : var ( -- password-field-placeholder-color ) ;
letter-spacing : 0 ;
letter-spacing : 0 ;
}
}
@ -138,38 +177,41 @@
display : inline-flex ;
display : inline-flex ;
align-items : center ;
align-items : center ;
justify-content : center ;
justify-content : center ;
inline-size : calc( var( --_password-field- fon t-size) * 1 . 5 ) ;
inline-size : var( --_password-field- trigger -size) ;
block-size : calc( var( --_password-field- fon t-size) * 1 . 5 ) ;
block-size : var( --_password-field- trigger -size) ;
margin-block : auto ;
margin-block : auto ;
padding : 0 ;
padding : 0 ;
border : 0 ;
border : var( --password-field-trigger-border-width ) solid var ( --password-field-trigger-border ) ;
border-radius : var ( -- radius-sm ) ;
border-radius : var ( -- password-field-trigger- radius) ;
background : transparent ;
background : var( --password-field-trigger-bg ) ;
color : var ( -- _password-field-muted ) ;
color : var ( -- password-field-trigger-color ) ;
cursor : pointer ;
cursor : pointer ;
transition :
transition :
color var ( --duration-fast ) var ( --ease-default ) ,
background var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ,
background var ( --duration-fast ) var ( --ease-default ) ;
border-color var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ,
color var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ,
box-shadow var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ;
}
}
[ data-password-field-visibility-trigger ] : hover {
[ data-password-field-visibility-trigger ] : hover : not ( [ data-disabled ] ) {
color : var ( --_password-field-color ) ;
background : var ( --password-field-trigger-bg-hover ) ;
background : color-mix ( in srgb , var ( --_password-field-border ) 30 % , transparent ) ;
border-color : var ( --password-field-trigger-border-hover ) ;
color : var ( --password-field-trigger-color-hover ) ;
}
}
[ data-password-field-visibility-trigger ] : focus-visible {
[ data-password-field-visibility-trigger ] : focus-visible {
outline : var( --ring-width , 2px ) solid var ( --_password-field-border-focus ) ;
outline : none ;
outline-offset: 1px ;
box-shadow: var ( --password-field-trigger-focus-shadow ) ;
}
}
[ data-password-field-visibility-trigger ] [ data-disabled ] {
[ data-password-field-visibility-trigger ] [ data-disabled ] {
opacity : 0. 5 ;
opacity : var( --password-field-disabled-opacity ) ;
cursor : not-allowed ;
cursor : not-allowed ;
}
}
[ data-password-field-visibility-trigger ] svg {
[ data-password-field-visibility-trigger ] svg {
inline-size : calc ( var ( --_password-field- font-size) * 1 . 1 ) ;
inline-size : calc ( var ( --_password-field- trigger-size) - 8px ) ;
block-size : calc ( var ( --_password-field- font-size) * 1 . 1 ) ;
block-size : calc ( var ( --_password-field- trigger-size) - 8px ) ;
}
}
/* ── CapsLockIndicator ─────────────────────────────────────────────── */
/* ── CapsLockIndicator ─────────────────────────────────────────────── */
@ -183,20 +225,20 @@
padding-inline : 0 . 4em ;
padding-inline : 0 . 4em ;
padding-block : 0 . 2em ;
padding-block : 0 . 2em ;
border-radius : var ( --radius-sm ) ;
border-radius : var ( --radius-sm ) ;
font-size : calc( var( --_password-field- font-size) * 0 . 75 ) ;
font-size : var( --_password-field- caps- font-size) ;
font-weight : 600 ;
font-weight : var( --font-weight-medium , 500 ) ;
color : transparent ;
color : transparent ;
background : transparent ;
background : transparent ;
transition :
transition :
color var ( -- duration-fast) var ( --ease-default ) ,
color var ( -- password-field-transition-duration) var ( --password-field-transition-ease ) ,
background var ( -- duration-fast) var ( --ease-default ) ;
background var ( -- password-field-transition-duration) var ( --password-field-transition-ease ) ;
pointer-events : none ;
pointer-events : none ;
user-select : none ;
user-select : none ;
}
}
[ data-password-field-caps-lock-indicator ] [ data-active ] {
[ data-password-field-caps-lock-indicator ] [ data-active ] {
color : var ( -- color-risk-text ) ;
color : var ( -- password-field-caps-color ) ;
background : color-mix( in srgb , var ( --color-risk-track ) 50 % , transparent ) ;
background : var( --password-field-caps-bg ) ;
}
}
[ data-password-field-caps-lock-glyph ] {
[ data-password-field-caps-lock-glyph ] {
@ -204,7 +246,7 @@
line-height : 1 ;
line-height : 1 ;
}
}
/* ── StrengthMeter ──────────────────────────────────────────── ─────── */
/* ── StrengthMeter (composable; layout below the control row) ─────── */
[ data-password-field-strength-meter ] {
[ data-password-field-strength-meter ] {
flex : 1 1 100 % ;
flex : 1 1 100 % ;
@ -212,38 +254,42 @@
flex-direction : column ;
flex-direction : column ;
gap : 0 . 25em ;
gap : 0 . 25em ;
margin-top : var ( --space-1 ) ;
margin-top : var ( --space-1 ) ;
padding-inline : 0 ;
}
}
[ data-password-field-strength-meter-track ] {
[ data-password-field-strength-meter-track ] {
display : grid ;
display : grid ;
grid-template-columns : repeat ( 4 , 1fr ) ;
grid-template-columns : repeat ( 4 , 1fr ) ;
gap : 4px ;
gap : var( --password-field-strength-segment-gap ) ;
}
}
[ data-password-field-strength-meter-segment ] {
[ data-password-field-strength-meter-segment ] {
block-size : 4px ;
block-size : var ( --password-field-strength-segment-height ) ;
border-radius : var ( --radius-sm ) ;
border-radius : var ( --password-field-strength-radius ) ;
background : var ( --color-neutral-track ) ;
background : var ( --password-field-strength-segment-bg ) ;
transition : background var ( --duration-fast ) var ( --ease-default ) ;
transition :
background var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ;
}
}
[ data-password-field-strength-meter ] [ data-strength = '1' ] [ data-password-field-strength-meter-segment ] [ data-active ] {
[ data-password-field-strength-meter ] [ data-strength = '1' ]
[ data-password-field-strength-meter-segment ] [ data-active ] {
background : var ( --color-loss-solid , var ( --color-threat-solid ) ) ;
background : var ( --color-loss-solid , var ( --color-threat-solid ) ) ;
}
}
[ data-password-field-strength-meter ] [ data-strength = '2' ] [ data-password-field-strength-meter-segment ] [ data-active ] {
[ data-password-field-strength-meter ] [ data-strength = '2' ]
[ data-password-field-strength-meter-segment ] [ data-active ] {
background : var ( --color-risk-solid ) ;
background : var ( --color-risk-solid ) ;
}
}
[ data-password-field-strength-meter ] [ data-strength = '3' ] [ data-password-field-strength-meter-segment ] [ data-active ] {
[ data-password-field-strength-meter ] [ data-strength = '3' ]
[ data-password-field-strength-meter-segment ] [ data-active ] {
background : var ( --color-fulfill-solid ) ;
background : var ( --color-fulfill-solid ) ;
}
}
[ data-password-field-strength-meter ] [ data-strength = '4' ] [ data-password-field-strength-meter-segment ] [ data-active ] {
[ data-password-field-strength-meter ] [ data-strength = '4' ]
[ data-password-field-strength-meter-segment ] [ data-active ] {
background : var ( --color-affirm-solid ) ;
background : var ( --color-affirm-solid ) ;
}
}
[ data-password-field-strength-meter-label ] {
[ data-password-field-strength-meter-label ] {
font-size : calc ( var ( --_password-field-font-size ) * 0 . 7 5) ;
font-size : calc ( var ( --_password-field-font-size ) * 0 . 8 5) ;
color : var ( -- _password-field-muted ) ;
color : var ( -- password-field-placeholder-color ) ;
font-variant-numeric : tabular-nums ;
font-variant-numeric : tabular-nums ;
}
}