CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 2m57s
CI / Coding Standards (pull_request) Successful in 3m3s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
The Profile block is headed "Your profile", but the one person on it you could not change was yourself: your name, your birth year, and whether you take lessons yourself were fixed at whatever signup recorded, and correcting any of them meant asking a studio admin. A "Your details" section now opens the page, saved through the same nonce-checked template_redirect post/redirect/get path the child rows use: - Your name, written to display_name and nickname together, for the reason updateChild() does — UserName reads the nickname first, and leaving it behind would put the account's email address back on every screen that names a person. - "I take lessons myself", the positive of us_guardian_only. This makes good on the claim already in bookableStudents() and the feature doc that a guardian-only account can put itself right from the profile page. - Your birth year, held to the same normaliseBirthYear() rule as every other student. The email is shown but not editable: it is the account's user_login as well as its address, so changing it stays a studio-side job. The birth-year field deliberately carries no `required` attribute. It is asked of a student only, and this page loads no JavaScript, so a browser-enforced `required` would leave a guardian who books solely for other people unable to submit the form at all; handleSelf() enforces it against the checkbox instead. Unticking the box does not clear a stored birth year — it says who books, not "forget what is on file". Closes #165 Co-Authored-By: Claude Opus 5 <[email protected]>
698 lines
15 KiB
CSS
698 lines
15 KiB
CSS
.us-login-form label {
|
|
display: block;
|
|
margin-bottom: 4px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.us-login-form input[type="text"],
|
|
.us-login-form input[type="password"] {
|
|
width: 100%;
|
|
max-width: 340px;
|
|
padding: 8px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.us-error {
|
|
color: #c00;
|
|
border-left: 4px solid #c00;
|
|
padding-left: 8px;
|
|
}
|
|
|
|
#us-booking-app .us-slot {
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 12px 16px;
|
|
margin-bottom: 8px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
#us-booking-error {
|
|
color: #c00;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
/*
|
|
* The upcoming-lessons panel. Every rule here is scoped under #us-booking-app —
|
|
* the same id-level specificity .us-slot above uses — because these rows sit in
|
|
* whatever layout the theme provides and carry more content than a calendar
|
|
* cell. Bare class selectors lost to theme rules on div/span/strong, which
|
|
* collapsed the flex layout and piled the details on top of the actions.
|
|
*/
|
|
#us-booking-app .us-my-lessons {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson {
|
|
box-sizing: border-box;
|
|
max-width: 100%;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 12px 16px;
|
|
margin-bottom: 8px;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 8px 12px;
|
|
}
|
|
|
|
/*
|
|
* `min-width: 0` lets the title column shrink below its content width — without
|
|
* it a long offering title cannot compress and shoves the status pill and
|
|
* Cancel button out of the row. The flex-basis keeps the details and the
|
|
* actions on one line while there is room, and wraps them once there is not.
|
|
*/
|
|
#us-booking-app .us-my-lesson-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
flex: 1 1 14em;
|
|
min-width: 0;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson-title,
|
|
#us-booking-app .us-my-lesson-when {
|
|
overflow-wrap: break-word;
|
|
word-break: break-word;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson-title {
|
|
font-size: 1.05em;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson-duration {
|
|
font-weight: normal;
|
|
color: #666;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson-when {
|
|
color: #555;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px 12px;
|
|
align-items: center;
|
|
}
|
|
|
|
/*
|
|
* Theme-proofing for the leaf text. The row and its two columns are divs with
|
|
* explicit flex rules above, but the text itself still sits in inline elements
|
|
* a theme is free to take out of normal flow — an absolutely positioned,
|
|
* floated or negatively offset span drops the date/time on top of the title and
|
|
* the status pill on top of the Cancel button. Pinning the properties that
|
|
* would have to change keeps the leaves in flow, at the same id-level
|
|
* specificity the rules above rely on.
|
|
*
|
|
* `line-height` is pinned for the same reason and is the subtler one, because a
|
|
* theme does not have to target this panel to break it — it is inherited, so a
|
|
* `line-height: 0` anywhere above (the usual icon-font or sprite reset) reaches
|
|
* these elements untouched. Below 1 it produces both halves of the same bug: a
|
|
* line box shorter than its glyphs, so stacked lines in the details column
|
|
* overlap, and an inline-block pill whose background is shorter than the text
|
|
* sitting in it. Nothing here should ever inherit a line-height, so the panel
|
|
* states its own.
|
|
*/
|
|
#us-booking-app .us-my-lesson,
|
|
#us-booking-app .us-my-lesson-info,
|
|
#us-booking-app .us-my-lesson-actions,
|
|
#us-booking-app .us-my-lesson-title,
|
|
#us-booking-app .us-my-lesson-when,
|
|
#us-booking-app .us-my-lesson-duration,
|
|
#us-booking-app .us-my-lesson-who,
|
|
#us-booking-app .us-my-lesson-kind,
|
|
#us-booking-app .us-lesson-status {
|
|
line-height: 1.45;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson-title,
|
|
#us-booking-app .us-my-lesson-when,
|
|
#us-booking-app .us-my-lesson-duration,
|
|
#us-booking-app .us-my-lesson-who,
|
|
#us-booking-app .us-my-lesson-kind,
|
|
#us-booking-app .us-lesson-status {
|
|
position: static;
|
|
float: none;
|
|
margin: 0;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
/*
|
|
* The rows the "Show all" button reveals. `[hidden]` is only a UA-stylesheet
|
|
* rule, so any author rule setting a display on div beats it — the html5-reset
|
|
* `div { display: block }` is still widespread in themes — and the rows the
|
|
* button is meant to gate render anyway. An author !important is the only way
|
|
* to win that cascade.
|
|
*/
|
|
#us-booking-app [hidden] {
|
|
display: none !important;
|
|
}
|
|
|
|
#us-booking-app .us-show-all-lessons {
|
|
background: transparent;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
padding: 6px 14px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
#us-booking-app .us-show-all-lessons:hover {
|
|
border-color: #888;
|
|
}
|
|
|
|
#us-booking-app .us-cancel-lesson {
|
|
background: transparent;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
padding: 4px 12px;
|
|
cursor: pointer;
|
|
color: #c00;
|
|
}
|
|
|
|
#us-booking-app .us-cancel-lesson:hover {
|
|
border-color: #c00;
|
|
}
|
|
|
|
#us-booking-app .us-lesson-status {
|
|
display: inline-block;
|
|
font-size: 0.85em;
|
|
font-weight: 600;
|
|
padding: 2px 10px;
|
|
border-radius: 10px;
|
|
background: #eee;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
#us-booking-app .us-lesson-status-confirmed {
|
|
background: #e2f5e5;
|
|
color: #1a7d2e;
|
|
}
|
|
|
|
#us-booking-app .us-lesson-status-pending {
|
|
background: #fdf3d7;
|
|
color: #8a6d1a;
|
|
}
|
|
|
|
.us-calendar-controls {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.us-filter-toggle {
|
|
padding: 6px 16px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.us-filter-toggle.us-active {
|
|
background: #333;
|
|
border-color: #333;
|
|
color: #fff;
|
|
}
|
|
|
|
.us-type-filter {
|
|
margin-bottom: 12px;
|
|
padding: 8px 12px;
|
|
border: 1px solid #eee;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.us-type-filter-heading {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.us-type-filter-choices {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 8px 16px;
|
|
}
|
|
|
|
.us-type-filter-choice {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.us-type-filter-clear {
|
|
margin-left: auto;
|
|
padding: 4px 12px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.us-view-toggle {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.us-view-toggle button {
|
|
padding: 6px 16px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.us-view-toggle button.us-active {
|
|
background: #333;
|
|
border-color: #333;
|
|
color: #fff;
|
|
}
|
|
|
|
.us-week-nav {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.us-week-nav button {
|
|
padding: 6px 12px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.us-week-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
gap: 8px;
|
|
}
|
|
|
|
.us-week-day {
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 8px;
|
|
min-height: 90px;
|
|
}
|
|
|
|
.us-week-day-heading {
|
|
margin: 0 0 8px;
|
|
font-size: 0.85em;
|
|
text-align: center;
|
|
}
|
|
|
|
.us-week-slot {
|
|
display: block;
|
|
width: 100%;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.us-week-empty {
|
|
display: block;
|
|
text-align: center;
|
|
opacity: 0.4;
|
|
}
|
|
|
|
/* The price and pay agreement on a booking / enrolment form. */
|
|
.us-price {
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 12px 16px;
|
|
margin: 16px 0;
|
|
}
|
|
|
|
.us-price h4 {
|
|
margin: 0 0 8px;
|
|
}
|
|
|
|
.us-price p {
|
|
margin: 0 0 4px;
|
|
}
|
|
|
|
.us-price-amount strong {
|
|
font-size: 1.15em;
|
|
}
|
|
|
|
.us-price-cadence {
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.us-price-tax,
|
|
.us-price-note {
|
|
font-size: 0.9em;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.us-price-agree {
|
|
display: block;
|
|
margin-top: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* The cadence-carrying price on a group-class card. */
|
|
.us-class-price {
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* Policy acceptance — booking, enrolment, and signup all render this markup. */
|
|
.us-policy {
|
|
margin: 16px 0;
|
|
}
|
|
|
|
.us-policy h4 {
|
|
margin: 0 0 6px;
|
|
}
|
|
|
|
/*
|
|
* The body is admin-authored HTML sitting inside whatever layout the theme
|
|
* provides, so it gets an explicit reading box rather than inheriting one.
|
|
* `overflow-wrap` breaks pasted URLs instead of letting one long token force
|
|
* the horizontal scrollbar, and the bounded height keeps a long policy from
|
|
* pushing the accept checkbox off the screen.
|
|
*/
|
|
.us-policy-body {
|
|
box-sizing: border-box;
|
|
max-width: 100%;
|
|
max-height: 260px;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
padding: 12px 14px;
|
|
margin-bottom: 8px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
background: #fafafa;
|
|
white-space: normal;
|
|
overflow-wrap: break-word;
|
|
word-break: break-word;
|
|
line-height: 1.5;
|
|
text-align: left;
|
|
}
|
|
|
|
.us-policy-body p,
|
|
.us-policy-body ul,
|
|
.us-policy-body ol {
|
|
margin: 0 0 0.75em;
|
|
max-width: 100%;
|
|
}
|
|
|
|
.us-policy-body ul,
|
|
.us-policy-body ol {
|
|
padding-left: 1.5em;
|
|
}
|
|
|
|
.us-policy-body > :last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.us-policy-accept,
|
|
.us-policies input[type="checkbox"] {
|
|
margin-right: 6px;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.us-week-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.us-week-day {
|
|
min-height: 0;
|
|
}
|
|
|
|
/*
|
|
* A lesson row carries a title, a date/time, a status pill and a button —
|
|
* more than fits one narrow line, so stack the details above the actions
|
|
* rather than letting them wrap into each other.
|
|
*/
|
|
#us-booking-app .us-my-lesson {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
#us-booking-app .us-my-lesson-info {
|
|
flex: 0 0 auto;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* "Who is this for?" picker — booking and enrolment. Present only on an account
|
|
* that books for more than one person, so it is styled as a normal field rather
|
|
* than a callout.
|
|
*/
|
|
.us-student-picker select {
|
|
max-width: 100%;
|
|
}
|
|
|
|
/*
|
|
* Whose lesson a row in the upcoming panel is — only shown on an account that
|
|
* books for more than one person. Scoped under #us-booking-app like the rest of
|
|
* the panel; as a bare class it was the one rule in the group a theme could
|
|
* outrank on a plain span.
|
|
*/
|
|
#us-booking-app .us-my-lesson-who {
|
|
font-weight: normal;
|
|
opacity: 0.75;
|
|
}
|
|
|
|
/*
|
|
* Marks a row in the upcoming panel as a group-class session. The list mixes
|
|
* one-to-one lessons and classes, and only the class rows have no Cancel button
|
|
* — without a label that reads as a missing button rather than a different kind
|
|
* of thing.
|
|
*/
|
|
#us-booking-app .us-my-lesson-kind {
|
|
display: inline-block;
|
|
margin-left: 6px;
|
|
padding: 1px 6px;
|
|
border-radius: 10px;
|
|
background: #eef1f5;
|
|
color: #3c434a;
|
|
font-size: 0.75em;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.03em;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
/*
|
|
* The signup form's grouped sections: who you are registering, your own
|
|
* details, and the students you are adding. One box style for all three so the
|
|
* form reads as a short list of decisions rather than an undifferentiated
|
|
* column of fields.
|
|
*/
|
|
.us-reg-group {
|
|
margin: 16px 0;
|
|
padding: 12px 14px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.us-reg-group legend {
|
|
padding: 0 6px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.us-children-intro {
|
|
margin-top: 0;
|
|
font-size: 0.9em;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
/*
|
|
* Each child is a bordered group so a family of three does not read as one long
|
|
* undifferentiated column of fields.
|
|
*/
|
|
.us-child {
|
|
margin-bottom: 12px;
|
|
padding: 10px 12px;
|
|
border-left: 3px solid #ddd;
|
|
background: #fafafa;
|
|
}
|
|
|
|
.us-child > p:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* The guardian's manage-children screen ([us_family]). */
|
|
|
|
/*
|
|
* The account holder's own details, set off from the students below so the two
|
|
* halves of the page do not read as one long form.
|
|
*/
|
|
.us-family-self {
|
|
margin-bottom: 24px;
|
|
padding-bottom: 16px;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.us-family-self-email span {
|
|
font-weight: 600;
|
|
}
|
|
|
|
/*
|
|
* Guidance under a control, not a label: it explains when a field matters
|
|
* rather than naming it, so it is sized down and reads after the input.
|
|
*/
|
|
.us-field-hint {
|
|
display: block;
|
|
margin-top: 4px;
|
|
font-size: 0.9em;
|
|
opacity: 0.75;
|
|
}
|
|
|
|
.us-family-list {
|
|
margin: 0 0 20px;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.us-family-child {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px 12px;
|
|
align-items: baseline;
|
|
padding: 10px 0;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.us-family-child-name {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.us-family-child-birth-year {
|
|
font-size: 0.9em;
|
|
opacity: 0.75;
|
|
}
|
|
|
|
/*
|
|
* The actions sit at the far end of the row. Remove is its own form (it posts),
|
|
* so it is forced inline rather than taking a block of its own.
|
|
*/
|
|
.us-family-child-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: baseline;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.us-family-remove {
|
|
display: inline;
|
|
}
|
|
|
|
/* The editing row replaces the child's line, so it spans the whole width. */
|
|
.us-family-edit {
|
|
width: 100%;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
/* A name, a date and two actions do not fit one narrow line. */
|
|
.us-family-child-actions {
|
|
margin-left: 0;
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The live password verdict under the signup field. Colour is a reinforcement,
|
|
* not the message — the text says what is wrong on its own, so this still reads
|
|
* correctly to anyone who cannot separate the hues.
|
|
*/
|
|
.us-password-strength {
|
|
display: block;
|
|
margin-top: 4px;
|
|
font-size: 0.85em;
|
|
}
|
|
|
|
.us-password-strength.is-short,
|
|
.us-password-strength.is-weak {
|
|
color: #c00;
|
|
}
|
|
|
|
.us-password-strength.is-medium {
|
|
color: #7a5c00;
|
|
}
|
|
|
|
.us-password-strength.is-strong {
|
|
color: #1a7d2e;
|
|
}
|
|
|
|
/*
|
|
* The account panel: who is signed in, and the way out. Sized to sit in a
|
|
* header or sidebar, so the rules stay minimal and inherit the theme's type —
|
|
* a block that lands in a site header should look like it belongs there.
|
|
*/
|
|
.us-account p {
|
|
margin: 0 0 4px;
|
|
}
|
|
|
|
.us-account-name {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.us-account-email {
|
|
display: block;
|
|
font-size: 0.9em;
|
|
opacity: 0.75;
|
|
}
|
|
|
|
.us-account-actions {
|
|
margin-top: 8px;
|
|
}
|
|
|
|
/*
|
|
* `[hidden]` is a UA-stylesheet rule, so the widespread `div { display: block }`
|
|
* theme reset outranks it — the same trap the upcoming-lessons panel hit. An
|
|
* author !important is the only way to win, and it has to sit before the
|
|
* display rule it guards against.
|
|
*/
|
|
.us-notice[hidden] {
|
|
display: none !important;
|
|
}
|
|
|
|
/*
|
|
* The "you're booked" / "you're enrolled" notice. It sits above the calendar
|
|
* or class list rather than replacing it, so it needs to read as a banner
|
|
* about something that just happened — not as the page's content.
|
|
*/
|
|
.us-notice {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 8px 16px;
|
|
margin-bottom: 16px;
|
|
padding: 12px 16px;
|
|
border: 1px solid #b7dfc0;
|
|
border-left-width: 4px;
|
|
border-radius: 4px;
|
|
background: #f2faf4;
|
|
color: #1a5c2a;
|
|
}
|
|
|
|
.us-notice p {
|
|
margin: 0;
|
|
}
|
|
|
|
.us-notice-dismiss {
|
|
background: transparent;
|
|
border: 1px solid currentColor;
|
|
border-radius: 4px;
|
|
padding: 4px 12px;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* Shown only in block-editor previews (see BlockPreview). */
|
|
.us-editor-note {
|
|
font-size: 0.85em;
|
|
font-style: italic;
|
|
opacity: 0.7;
|
|
}
|