The doubled edge was not the textarea's alone -- it was on every field, and the previous commit's explanation of why the inputs were exempt was wrong. I asserted that at 999px the ring and the outline clamp to the same curve without measuring it. They do not: ring 999px -> a 24px circle (half the field's height) outline 999px -> a 12x24 ellipse (leading piece width x half height) The ring is one continuous rounded rectangle over the whole field; the outline beneath it is split into three pieces and its leading piece is 12px wide. No radius makes a full-width curve agree with a 12px-wide one, so there was nothing to tune -- the ring had to go. It was mine, lifted from the library's `gradient` theme onto a shell that does not use it, and `alternative` already expresses focus through the outline's own colour. The textarea's radius goes back to 1.25rem, since it was only lowered to dodge the clamp. Verified at 3x, one curve per field: valid+focus violet, error+focus red, no ring anywhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
133 lines
5.1 KiB
SCSS
133 lines
5.1 KiB
SCSS
// The gap has to beat the distance from a field to its own error message,
|
|
// which Material renders flush underneath it — 0px. At 1.25rem the message
|
|
// ended up 6px from the *next* field's label and 0px from its own field, so
|
|
// "Falta el nombre" read as the caption of "HORAS POR SEMANA" rather than of
|
|
// the field above it. The three lines have to group with the field they belong
|
|
// to, which means the space between rows must be visibly larger than the space
|
|
// inside one.
|
|
.landing-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2rem;
|
|
}
|
|
|
|
.landing-form__kind {
|
|
border: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
|
|
legend {
|
|
padding: 0 0 0.5rem;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.landing-form__row {
|
|
display: grid;
|
|
gap: 1.25rem;
|
|
|
|
@media (min-width: 640px) {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
}
|
|
|
|
.landing-form__error {
|
|
margin: 0;
|
|
color: var(--pi-color-warn, #d64545);
|
|
}
|
|
|
|
// The hours field sits inside an @if, and that is what breaks its label.
|
|
//
|
|
// Angular Material measures an outlined field's floating label once, on init,
|
|
// and writes the result to the element as an inline
|
|
// `transform: var(--mat-mdc-form-field-label-transform, translateY(-50%) …)`.
|
|
// A field created inside a conditional block is measured before it has any
|
|
// geometry, so the value it keeps is the unfloated one: the label ends up 5px
|
|
// above the wrapper instead of 18, straddling the 2px outline and struck
|
|
// through by it.
|
|
//
|
|
// `@pi/ui` already publishes the right geometry for every other field
|
|
// (`translateY(-3rem) scale(1)`, a 48px label box), and that rule matches this
|
|
// one too — it simply loses to the inline style. `!important` is what beats an
|
|
// inline declaration, and it is restating the library's own values, not
|
|
// inventing new ones.
|
|
:host ::ng-deep .pi-field.pi-form-field__number {
|
|
.mdc-notched-outline .mdc-floating-label {
|
|
height: var(--pi-spacing-xxxx-large) !important;
|
|
}
|
|
|
|
.mdc-text-field:not(.mat-form-field-no-animations) .mdc-floating-label {
|
|
transform: translateY(-3rem) scale(1) !important;
|
|
}
|
|
}
|
|
|
|
// Two things the library gets wrong once a field is in a real state.
|
|
//
|
|
// 1. The label changes shape when you focus it. `@pi/ui` applies the theme's
|
|
// label typography under `:not(.mdc-text-field--focused)`, so text-transform,
|
|
// letter-spacing and weight all fall away the moment the field takes focus:
|
|
// "MENSAJE *" becomes "Mensaje *" in a lighter face, and the label visibly
|
|
// reflows under the cursor. Restated here without that guard. Colour is
|
|
// deliberately left alone — Material tints the label on focus, and that part
|
|
// is wanted.
|
|
//
|
|
// 2. The focus ring is a stadium on the textarea. Both the notched outline and
|
|
// the gradient ring take their radius from `--pi-theme-field-radius`, which
|
|
// the `alternative` shell sets to 999px. That is right for a 48px-tall input
|
|
// and wrong for a 112px-tall textarea: the two shapes stop coinciding and
|
|
// the field is drawn twice, once in the error colour and once in the
|
|
// gradient, offset from each other. A fixed radius makes them agree again.
|
|
:host ::ng-deep {
|
|
.pi-field .mdc-text-field:not(.mdc-text-field--disabled) .mdc-floating-label {
|
|
text-transform: var(--pi-theme-field-label-transform, none);
|
|
letter-spacing: var(--pi-theme-field-label-letter-spacing, normal);
|
|
font-weight: var(--pi-theme-field-label-weight, inherit);
|
|
}
|
|
|
|
// A stadium is right for a 48px input and wrong for a 112px textarea, which
|
|
// is all this is now — with the focus ring gone there is no second shape to
|
|
// agree with.
|
|
.pi-field.pi-form-field__textarea {
|
|
--pi-theme-field-radius: 1.25rem;
|
|
}
|
|
}
|
|
|
|
// Error states, which move the form around and read wrong.
|
|
//
|
|
// 1. Submitting an incomplete form shoved everything below it downwards.
|
|
// Material's subscript strip is 10px tall while a field is valid and 24px
|
|
// once it carries a message, so every errored field grew the form by 14px at
|
|
// once — the fields under the cursor jumped out from under it. Reserving the
|
|
// taller height up front costs 14px of whitespace and buys a form that does
|
|
// not move.
|
|
//
|
|
// 2. `pi-checkbox` renders its message as an inline span straight after the
|
|
// label, so it ran on as part of the sentence: "Acepto que guardéis mis
|
|
// datos para responderme. Hace falta tu consentimiento". It belongs on its
|
|
// own line, like every other field's message.
|
|
:host ::ng-deep {
|
|
.pi-field .mat-mdc-form-field-subscript-wrapper {
|
|
min-height: 1.5rem;
|
|
}
|
|
|
|
// Its own line, in space reserved for it. Left in flow it would push the
|
|
// submit button 25px down at the exact moment you press it — the same jump
|
|
// the reserved subscript above removes for the fields.
|
|
.pi-checkbox {
|
|
position: relative;
|
|
padding-bottom: 1.25rem;
|
|
}
|
|
|
|
// Nothing in the library styles this class, so the message inherited the
|
|
// body colour and read as ordinary text next to four red ones.
|
|
.pi-checkbox__error {
|
|
display: block;
|
|
position: absolute;
|
|
left: 0;
|
|
bottom: 0;
|
|
color: var(--pi-color-warn);
|
|
// `mat-error` renders the other four at 12px; this one inherited 15px.
|
|
font-size: var(--pi-font-size-x-small, 0.75rem);
|
|
}
|
|
}
|