@ -5,6 +5,10 @@
* 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 .
*
* Layout : CSS grid keeps the inner controls ( input , caps indicator ,
* visibility trigger ) on the first row and the StrengthMeter on a
* second row that sits OUTSIDE the bordered chrome .
* /
[ data-password-field ] {
@ -18,10 +22,10 @@
--_password-field-accent-track : var ( --color-primary-track ) ;
--_password-field-accent-text : var ( --color-primary-text ) ;
display : inline-flex ;
flex-wrap : wrap ;
display : grid ;
grid-template-columns : minmax ( 0 , 1fr ) auto auto ;
column-gap : var ( --_password-field-gap ) ;
align-items : center ;
gap : var ( --_password-field-gap ) ;
inline-size : 100 % ;
min-inline-size : 0 ;
block-size : var ( --_password-field-height ) ;
@ -39,12 +43,57 @@
box-shadow var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ;
}
/ * When the StrengthMeter is composed , it claims a second row and the
* height auto-grows ; otherwise the single-row chrome rules . * /
/ * When the StrengthMeter is composed , the root becomes a vertical stack :
* the bordered control row on top , the meter as a separate sibling below .
* Border + padding move to a synthetic :: before pseudo-row … but since the
* meter is a real child , the cleanest move is to lift the chrome to the
* inner cells . * /
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) {
display : grid ;
grid-template-columns : minmax ( 0 , 1fr ) auto auto ;
grid-template-rows : var ( --_password-field-height ) auto ;
row-gap : var ( --space-2 ) ;
block-size : auto ;
min-block-size : var ( --_password-field-height ) ;
padding-block : var ( --password-field-gap-sm ) ;
border : 0 ;
background : transparent ;
padding : 0 ;
box-shadow : none ;
}
/ * When the meter is present , the first row recreates the bordered chrome
* via a pseudo-element that spans all 3 columns . The inner controls keep
* their normal flow because the pseudo sits behind them ( z-index 0 ) . * /
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) :: before {
content : '' ;
grid-column : 1 / -1 ;
grid-row : 1 / 2 ;
border : var ( --password-field-border-width ) solid var ( --password-field-border ) ;
border-radius : var ( --password-field-radius ) ;
background : var ( --password-field-bg ) ;
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 ) ;
pointer-events : none ;
}
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) [ data-password-field-input ] ,
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) [ data-password-field-visibility-trigger ] ,
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) [ data-password-field-caps-lock-indicator ] {
grid-row : 1 / 2 ;
padding-inline-start : 0 ;
z-index : 1 ;
}
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) [ data-password-field-input ] {
grid-column : 1 / 2 ;
padding-inline-start : var ( --_password-field-px ) ;
}
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) [ data-password-field-caps-lock-indicator ] {
grid-column : 2 / 3 ;
}
[ data-password-field ] : has ( [ data-password-field-strength-meter ] ) [ data-password-field-visibility-trigger ] {
grid-column : 3 / 4 ;
margin-inline-end : calc ( var ( --_password-field-px ) - var ( --_password-field-gap ) ) ;
}
/* ── Color accents ─────────────────────────────────────────────────── */
@ -125,12 +174,19 @@
[ data-password-field ] [ data-variant = 'outline' ] {
background : transparent ;
}
[ data-password-field ] [ data-variant = 'outline' ] : has ( [ data-password-field-strength-meter ] ) :: before {
background : transparent ;
}
[ data-password-field ] [ data-variant = 'ghost' ] {
border-color : transparent ;
background : transparent ;
padding-inline : 0 ;
}
[ data-password-field ] [ data-variant = 'ghost' ] : has ( [ data-password-field-strength-meter ] ) :: before {
border-color : transparent ;
background : transparent ;
}
/* ── State chrome ──────────────────────────────────────────────────── */
@ -138,10 +194,17 @@
border-color : var ( --password-field-border-focus ) ;
box-shadow : var ( --password-field-focus-shadow ) ;
}
[ data-password-field ] [ data-focused ] : has ( [ data-password-field-strength-meter ] ) :: before {
border-color : var ( --password-field-border-focus ) ;
box-shadow : var ( --password-field-focus-shadow ) ;
}
[ data-password-field ] [ data-invalid ] {
border-color : var ( --password-field-border-invalid ) ;
}
[ data-password-field ] [ data-invalid ] : has ( [ data-password-field-strength-meter ] ) :: before {
border-color : var ( --password-field-border-invalid ) ;
}
[ data-password-field ] [ data-disabled ] {
opacity : var ( --password-field-disabled-opacity ) ;
@ -150,8 +213,8 @@
/* ── Input ─────────────────────────────────────────────────────────── */
[ data-password-field-input ] {
flex : 1 1 auto ;
min-inline-size : 0 ;
inline-size : 100 % ;
border : 0 ;
background : transparent ;
color : inherit ;
@ -173,13 +236,11 @@
/* ── VisibilityTrigger ─────────────────────────────────────────────── */
[ data-password-field-visibility-trigger ] {
flex : 0 0 auto ;
display : inline-flex ;
align-items : center ;
justify-content : center ;
inline-size : var ( --_password-field-trigger-size ) ;
block-size : var ( --_password-field-trigger-size ) ;
margin-block : auto ;
padding : 0 ;
border : var ( --password-field-trigger-border-width ) solid var ( --password-field-trigger-border ) ;
border-radius : var ( --password-field-trigger-radius ) ;
@ -216,29 +277,25 @@
/* ── CapsLockIndicator ─────────────────────────────────────────────── */
/* Hidden by default. Only visible when caps lock is on. */
[ data-password-field-caps-lock-indicator ] {
flex : 0 0 auto ;
display : inline-flex ;
display : none ;
align-items : center ;
gap : 0 . 25em ;
margin-block : auto ;
padding-inline : 0 . 4em ;
padding-block : 0 . 2em ;
border-radius : var ( --radius-sm ) ;
font-size : var ( --_password-field-caps-font-size ) ;
font-weight : var ( --font-weight-medium , 500 ) ;
color : transparent ;
background : transparent ;
transition :
color var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ,
background var ( --password-field-transition-duration ) var ( --password-field-transition-ease ) ;
color : var ( --password-field-caps-color ) ;
background : var ( --password-field-caps-bg ) ;
pointer-events : none ;
user-select : none ;
white-space : nowrap ;
}
[ data-password-field-caps-lock-indicator ] [ data-active ] {
color : var ( --password-field-caps-color ) ;
background : var ( --password-field-caps-bg ) ;
display : inline-flex ;
}
[ data-password-field-caps-lock-glyph ] {
@ -249,11 +306,12 @@
/* ── StrengthMeter (composable; layout below the control row) ─────── */
[ data-password-field-strength-meter ] {
flex : 1 1 100 % ;
display : flex ;
flex-direction : column ;
gap : 0 . 25em ;
margin-top : var ( --space-1 ) ;
grid-column : 1 / -1 ;
grid-row : 2 / 3 ;
padding-inline : var ( --_password-field-px ) ;
}
[ data-password-field-strength-meter-track ] {