Commit ef8a9ce420a for woocommerce
commit ef8a9ce420a97f8aebf60a87e21bfd16d1cdf984
Author: Jill Q. <jill.quek@automattic.com>
Date: Thu Jul 2 00:45:22 2026 +0800
Align Order Edit meta boxes with WordPress 7.0 design conventions (#64564)
* Order data header: align typography and spacing with WP 7.0
The order edit page header was the most visually dated panel on the
screen. This commit modernizes typography, tightens spacing, and
fixes two literal-character oddities in the surrounding markup.
Typography:
- Drop HelveticaNeue-Light font-family on h2 and order_number
(system font stack inherits from WP admin)
- Drop text-shadow on h2 (a 2014 hack)
- h2: 21px regular -> 20px semibold (matches WP 7.0 page headings)
- order_number meta line: 16px -> 13px (reads as muted metadata,
not a subheading)
- p { color: #777 } -> color: $subtext (#767676), AA-passing on
white and consistent with helper-text color used elsewhere
Spacing:
- Section heads (h3, h4) margin-top: 1.33em -> 12px (tightens the
gap between the header and the General/Billing/Shipping column
heads to match WP admin's compact rhythm)
- order_data_column: margin-bottom: 24px on non-last columns so
they have breathing room when they stack at narrow widths
(no-op on float layouts that fit side-by-side)
- Date-picker input: margin-right: 8px to space it from the
hour/minute inputs after dropping the literal "@" connector
Copy:
- "Order #1234 details" -> "Order #1234" ("details" was redundant
with the surrounding panel context)
- "Paid on Apr 30, 2026 @ 9:30 am" -> "Paid on Apr 30, 2026 at
9:30 am" (every other date+time string in WC uses "at"; this
was the only "@" outlier)
- Form Date created field: dropped the literal "@" between the
date input and the hour input. WP block editor presents date
and time as separate visual blocks with no connector; aligning
with that direction.
* Order data: address read-only display polish + WP 7.0 alignment
Improvements to the billing/shipping read-only address blocks and
the surrounding column layout, focused on hierarchy, accessibility,
and resilience to long values.
Address blocks:
- 8px breathing room between the Billing/Shipping h3 and the
address content below
- Address paragraphs: 8px gap + 1.6 line-height for vertical rhythm
- Address field labels (Email:, Phone:, etc.): semibold + #1e1e1e
for clearer hierarchy against the values
- overflow-wrap: anywhere on address paragraphs so long un-breakable
values (long emails, long town names) wrap at the column boundary
instead of overflowing into the neighbor column
Empty state:
- Drop the redundant "Address:" prefix on the empty state — it now
reads "No billing address set." / "No shipping address set."
rather than "Address: No billing address set."
- Empty-state color: #999 -> $subtext (#767676) — passes WCAG AA at
4.51:1 instead of failing at 2.85:1
Edit pencil icon:
- Default color: #999 -> $subtext (consistent with helper-text gray)
- Hover/focus: #000 -> var(--wp-admin-theme-color) — picks up the
user's admin color scheme (Modern, Fresh, Ocean, etc.) instead
of always going black
Column layout:
- Slightly more breathing room between the General/Billing/Shipping
columns at tablet-ish viewports: gap 2% -> 3%, column 32% -> 31%
* Order data: align field labels and General-column Select2s with WP 7.0
Status + Customer dropdowns:
- Match the 40px WP 7.0 input height for Select2s in the General column
(Status, Customer). Mirrors the address-field treatment Annchichi
shipped in #64389; uses a `>` direct-child selector to scope to the
General column without overlapping her override on the address forms.
Field labels:
- Drop colons on "Date created", "Status", "Customer" — matches the
colon-free pattern from the Order notes meta box (PR #64476).
- Swap label `padding: 0 0 3px` for `margin-bottom: 4px` — same
spacing rhythm as the order notes labels, so the two meta boxes
feel consistent.
* Order Actions sidebar: replace float layout with flex
The Order Actions meta box (right column on the order edit page)
was built with floats that had two specific symptoms on WP 7.0
inputs:
- The Apply button was hardcoded to width: 24px, rendering as a
squished pill regardless of viewport.
- The Choose-action select was hardcoded to width: 225px, which
overflowed the sidebar at narrow widths.
Replaces with a flex-column outer + flex-row inner pattern:
- Outer .order_actions: display: flex; flex-direction: column;
list-style: none. Each li separated by a single border-top
divider with consistent 12px padding (mirrors the order notes
meta box treatment).
- Action row (#actions): flex with 8px gap. Select grows to fill
available width (flex: 1 with min-width: 0 to prevent flex
blow-out on long option labels). Apply button stays at content
size (flex-shrink: 0).
- Save row (.wide:not(#actions)): flex with justify-content:
space-between, flex-wrap so the Trash link + Update button wrap
cleanly when the row narrows.
Also drops the legacy default-li rules (50% width, float, text-align
center, sandwich-divider with white top + #ddd bottom) — they only
applied to non-.wide li's that don't exist in the markup; plugin-
injected li's now inherit the cleaner padding-and-divider style.
The border-top hex is currently the literal #dcdcde (WP gray-200);
will tokenize once #64476 lands the gray scale on trunk.
* Order data: render empty-state for missing billing email
Background:
The billing field loop renders each field as
`<p><strong>Label:</strong> value</p>`, gated by `if ( $field_value )`
so empty fields are silently dropped. Email is special-cased BEFORE
that empty check — it gets wrapped in a mailto: anchor regardless
of value. An empty email therefore becomes the truthy string
`<a href="mailto:"></a>` and renders as `Email address:` with
nothing visible after it.
Fix:
Capture whether the raw value was empty before applying the email
wrapper. If empty, render `<span class="none_set">No email address
set.</span>` instead of the empty mailto anchor. The .none_set
styling is already AA-compliant ($subtext) from commit 2.
Phone is intentionally left as-is — wc_make_phone_clickable returns
empty for empty input, so phone is silently dropped by the existing
truthy guard, which is consistent with other optional fields.
* Add changefile(s) from automation for the following project(s): woocommerce
* Address review: :has() empty-state fix, WPDS hover wash, comment cleanup
- Hide #delete-action via :not(:has(*)) — :empty never matched because the
PHP leaves whitespace inside the div (jorgeatorres)
- Destructive hover now matches WP 7.0: #f6e6e3 error wash + darkened stroke,
values from WPDS token fallbacks (jorgeatorres)
- Extra 8px separation between Update and Move to trash to reduce
accidental clicks (jorgeatorres)
- wc-order-save-action: id → class (Aljullu)
- is_string() instead of is_scalar() for billing email (Aljullu)
- Replace inaccurate .address strong color comment with explicit $gray-900
(Aljullu)
- Trim over-specific CSS comments: PR references, line numbers, vendor names
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Use $gray-900 token for heading and label colors, fix changelog newline
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add changefile(s) from automation for the following project(s): woocommerce
* Move order trash into a "more actions" menu; add extensibility filter
Move the destructive trash/delete action off the primary save row into a
kebab "more actions" menu, aligning the order actions box with the
WordPress unified admin header pattern so the prominent button no longer
sits next to Save where accidental clicks are likely.
Add the woocommerce_order_actions_menu_items filter so extensions can add
their own items to the menu (rendered with the shared neutral styling and
keyboard accessibility), with the trash action as the built-in default.
Includes a dependency-free, accessible menu controller (toggle, Escape,
outside-click, roving arrow-key focus).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add changefile(s) from automation for the following project(s): woocommerce
* Add changefile(s) from automation for the following project(s): woocommerce
* Order actions menu: size the dropdown to its content
Drop the fixed 200px min-width on the "more actions" menu so it hugs its
items (which are white-space: nowrap) instead of stretching a short label
like "Move to trash" across the full width. Keeps a small 120px floor.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Order actions: keep the stock-notification save row from regressing
The customer stock-notification edit/create screens share this .order_actions
submitbox markup but keep the legacy save row (#delete-action + .save_order,
no kebab). This PR removed the old float rules that laid that row out, so add
a small guard, scoped via :has(#delete-action), to restore its original
side-by-side layout. The guard can't match the order screen or extension-
injected `<li class="wide">` panels. Full migration of those templates to the
new design is left to a follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Order actions menu: align kebab a11y with Gutenberg
Four small accessibility fixes to the "more actions" menu, each matching how
@wordpress/components handles it:
- Menu-item focus: add `outline: 2px solid transparent` alongside the box-shadow
ring so a focus indicator survives forced-colors / Windows High Contrast (where
box-shadow is ignored). Drops the `outline: none` that suppressed it.
- Kebab icon: drive the SVG colour via `currentColor` from a neutral button
colour (not the secondary-button blue) and swap to the `CanvasText` system
colour in forced-colors, so the icon stays visible in high-contrast modes.
- Up Arrow on the toggle now opens onto the last menu item (Down opens onto the
first), per the APG menu-button pattern. Latent until the filter adds items.
- Close the menu on `focusout` rather than synchronously on Tab, so the focused
element isn't hidden out from under the browser (which could drop focus to the
<body> in Safari/Firefox). Mirrors Gutenberg's Dropdown closeIfFocusOutside.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add changefile(s) from automation for the following project(s): woocommerce
* Address order actions review feedback
* Add changefile(s) from automation for the following project(s): woocommerce
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/64564-sprinkle-order-edit-wp7-alignment b/plugins/woocommerce/changelog/64564-sprinkle-order-edit-wp7-alignment
new file mode 100644
index 00000000000..136b75eec77
--- /dev/null
+++ b/plugins/woocommerce/changelog/64564-sprinkle-order-edit-wp7-alignment
@@ -0,0 +1,4 @@
+Significance: minor
+Type: tweak
+
+Align the Order Edit meta boxes with WordPress 7.0 admin design and preserve the legacy order trash action surface with sentence-case copy.
\ No newline at end of file
diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss
index 3973f47f11d..ae8f08fe302 100644
--- a/plugins/woocommerce/client/legacy/css/admin.scss
+++ b/plugins/woocommerce/client/legacy/css/admin.scss
@@ -1901,12 +1901,9 @@ ul.wc_coupon_list_block {
h2 {
margin: 0;
- font-family: "HelveticaNeue-Light", "Helvetica Neue Light",
- "Helvetica Neue", sans-serif;
- font-size: 21px;
- font-weight: normal;
- line-height: 1.2;
- text-shadow: 1px 1px 1px white;
+ font-size: 20px;
+ font-weight: 600;
+ line-height: 1.3;
padding: 0;
}
@@ -1916,21 +1913,19 @@ ul.wc_coupon_list_block {
h3,
h4 {
- color: #333;
- margin: 1.33em 0 0;
+ color: $gray-900;
+ margin: 12px 0 0;
}
p {
- color: $gray-900;
+ color: $subtext;
}
p.order_number {
margin: 0;
- font-family: "HelveticaNeue-Light", "Helvetica Neue Light",
- "Helvetica Neue", sans-serif;
font-weight: normal;
- line-height: 1.6em;
- font-size: 16px;
+ line-height: 1.5;
+ font-size: 13px;
}
.order_data_header {
@@ -1956,8 +1951,8 @@ ul.wc_coupon_list_block {
}
.order_data_column {
- width: 32%;
- padding: 0 2% 0 0;
+ width: 31%;
+ padding: 0 3% 0 0;
float: left;
> h3 span {
@@ -1968,12 +1963,31 @@ ul.wc_coupon_list_block {
padding-right: 0;
}
+ // Breathing room when columns stack at narrow widths.
+ // (No-op on float layouts that fit side-by-side.)
+ &:not(:last-child) {
+ margin-bottom: 24px;
+ }
+
p {
padding: 0 !important;
}
- .address strong {
- display: block;
+ .address {
+ margin-top: 8px;
+
+ p {
+ margin: 0 0 8px;
+ line-height: 1.6;
+ overflow-wrap: anywhere;
+ }
+
+ strong {
+ display: block;
+ margin-bottom: 4px;
+ font-weight: 600;
+ color: $gray-900; // Field labels use the primary admin text color.
+ }
}
.form-field {
@@ -1985,7 +1999,8 @@ ul.wc_coupon_list_block {
label {
display: block;
- padding: 0 0 3px;
+ margin-bottom: 4px;
+ color: $gray-900;
}
input:not(.checkbox),
@@ -2040,6 +2055,7 @@ ul.wc_coupon_list_block {
}
input.date-picker {
width: 40%;
+ margin-right: 8px;
}
input.hour,
input.minute {
@@ -2047,8 +2063,8 @@ ul.wc_coupon_list_block {
}
}
- p.none_set {
- color: $gray-900;
+ .none_set {
+ color: $subtext;
}
div.edit_address {
@@ -2082,13 +2098,13 @@ ul.wc_coupon_list_block {
margin: 0 0 0 6px;
overflow: hidden;
position: relative;
- color: #999;
+ color: $subtext;
border: 0;
float: right;
&:hover,
&:focus {
- color: #000;
+ color: var(--wp-admin-theme-color, #007cba);
}
&::after {
@@ -2127,6 +2143,8 @@ ul.wc_coupon_list_block {
margin: 0;
overflow: hidden;
zoom: 1;
+ padding: 0;
+ list-style: none;
li {
border-top: 1px solid #fff;
@@ -2182,6 +2200,84 @@ ul.wc_coupon_list_block {
}
}
+body.post-type-shop_order,
+body.woocommerce_page_wc-orders {
+ .order_actions {
+ display: flex;
+ flex-direction: column;
+ overflow: visible;
+
+ li {
+ border-top: 1px solid $gray-200;
+ border-bottom: 0;
+ padding: 16px 12px;
+ float: none;
+ width: auto;
+ text-align: left;
+
+ &:first-child {
+ border-top: 0;
+ }
+
+ &.wide {
+ padding: 16px 12px;
+ }
+
+ // Save row: keep the legacy trash link in its usual position while
+ // clearing old button margins that overflow under WP 7.0 sizing.
+ &.wide:has(.save_order) {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+
+ #delete-action {
+ line-height: 40px;
+ text-align: left;
+ float: none;
+
+ &:not(:has(*)) {
+ display: none;
+ }
+ }
+
+ .save_order {
+ flex-shrink: 0;
+ margin: 0 0 0 auto;
+ float: none;
+ }
+ }
+
+ // Action row (top): select + Apply button. Replaces the legacy
+ // float layout where the Apply button was forced to width: 24px
+ // (a squished pill) and the select to a hardcoded 225px (overflowed
+ // on narrow sidebars).
+ &#actions {
+ display: flex;
+ gap: 8px;
+ align-items: stretch;
+
+ select {
+ flex: 1;
+ min-width: 0; // prevent flex blow-out on long option labels.
+ width: auto;
+ box-sizing: border-box;
+ float: none;
+ }
+
+ .button {
+ flex-shrink: 0;
+ width: auto;
+ box-sizing: border-box;
+ // Reset the legacy 1px button margin so the row doesn't overflow.
+ margin: 0;
+ float: none;
+ }
+ }
+ }
+ }
+}
+
#woocommerce-order-items {
.inside {
margin: 0;
@@ -2901,7 +2997,6 @@ ul.wc_coupon_list_block {
padding: 0;
ul.order_actions li {
- padding: 6px 10px;
box-sizing: border-box;
&:last-child {
@@ -7652,6 +7747,9 @@ table.bar_chart {
&:first-child {
width: 100%;
+ // Reset the base 3% gutter so the full-width first column
+ // doesn't render at 103% and overflow at narrow viewports.
+ padding-right: 0;
}
}
}
@@ -8398,6 +8496,22 @@ table.bar_chart {
}
}
+ // General column Select2s (Status + Customer): same WP 7.0 sizing as above.
+ // The direct-child `>` scopes this to General — address fields are nested
+ // inside .edit_address and covered by the rule above.
+ #order_data .order_data_column > .form-field-wide .select2-container .select2-selection--single {
+ height: 40px;
+
+ .select2-selection__rendered {
+ line-height: 38px;
+ padding: 0 12px;
+ }
+
+ .select2-selection__arrow {
+ height: 38px;
+ }
+ }
+
// Table action icon buttons: prevent WP 7.0 min-height from stretching them.
.widefat {
.column-wc_actions {
@@ -8437,17 +8551,6 @@ table.bar_chart {
}
- // Order actions: select + button overflow container due to WP 7.0 select margins.
- .order_actions {
- #actions select {
- width: calc(100% - 32px);
- }
-
- // Move to trash: make sure it aligns better vertically with 7.0's increased button height.
- #delete-action {
- line-height: 35px;
- }
- }
// Order actions reload button: icon line-height must match WP 7.0 button height.
.button.wc-reload {
diff --git a/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php b/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php
index 6fcc4464d50..60b3d841977 100644
--- a/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php
+++ b/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php
@@ -66,7 +66,7 @@ class WC_Meta_Box_Order_Actions {
if ( ! EMPTY_TRASH_DAYS ) {
$delete_text = __( 'Delete permanently', 'woocommerce' );
} else {
- $delete_text = __( 'Move to Trash', 'woocommerce' );
+ $delete_text = __( 'Move to trash', 'woocommerce' );
}
?>
<a class="submitdelete deletion" href="<?php echo esc_url( self::get_trash_or_delete_order_link( $order_id ) ); ?>"><?php echo esc_html( $delete_text ); ?></a>
diff --git a/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-data.php b/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-data.php
index b59a236be41..fafe0da7833 100644
--- a/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-data.php
+++ b/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-data.php
@@ -221,7 +221,7 @@ class WC_Meta_Box_Order_Data {
printf(
/* translators: 1: order type 2: order number */
- esc_html__( '%1$s #%2$s details', 'woocommerce' ),
+ esc_html__( '%1$s #%2$s', 'woocommerce' ),
esc_html( $order_type_object->labels->singular_name ),
esc_html( $order->get_order_number() )
);
@@ -261,7 +261,7 @@ class WC_Meta_Box_Order_Data {
if ( $order->get_date_paid() ) {
$meta_list[] = sprintf(
/* translators: 1: date 2: time */
- __( 'Paid on %1$s @ %2$s', 'woocommerce' ),
+ __( 'Paid on %1$s at %2$s', 'woocommerce' ),
wc_format_datetime( $order->get_date_paid() ),
wc_format_datetime( $order->get_date_paid(), get_option( 'time_format' ) )
);
@@ -317,8 +317,8 @@ class WC_Meta_Box_Order_Data {
<?php
$order_date_created_localised = ! is_null( $order->get_date_created() ) ? $order->get_date_created()->getOffsetTimestamp() : '';
?>
- <label for="order_date"><?php esc_html_e( 'Date created:', 'woocommerce' ); ?></label>
- <input type="text" class="date-picker" name="order_date" maxlength="10" value="<?php echo esc_attr( date_i18n( 'Y-m-d', $order_date_created_localised ) ); ?>" pattern="<?php echo esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment ?>" />@
+ <label for="order_date"><?php esc_html_e( 'Date created', 'woocommerce' ); ?></label>
+ <input type="text" class="date-picker" name="order_date" maxlength="10" value="<?php echo esc_attr( date_i18n( 'Y-m-d', $order_date_created_localised ) ); ?>" pattern="<?php echo esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment ?>" />
‎
<input type="number" class="hour" placeholder="<?php esc_attr_e( 'h', 'woocommerce' ); ?>" name="order_date_hour" min="0" max="23" step="1" value="<?php echo esc_attr( date_i18n( 'H', $order_date_created_localised ) ); ?>" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:
<input type="number" class="minute" placeholder="<?php esc_attr_e( 'm', 'woocommerce' ); ?>" name="order_date_minute" min="0" max="59" step="1" value="<?php echo esc_attr( date_i18n( 'i', $order_date_created_localised ) ); ?>" pattern="[0-5]{1}[0-9]{1}" />
@@ -328,7 +328,7 @@ class WC_Meta_Box_Order_Data {
<p class="form-field form-field-wide wc-order-status">
<label for="order_status">
<?php
- esc_html_e( 'Status:', 'woocommerce' );
+ esc_html_e( 'Status', 'woocommerce' );
if ( $order->needs_payment() ) {
printf(
'<a href="%s">%s</a>',
@@ -352,7 +352,7 @@ class WC_Meta_Box_Order_Data {
<!--email_off--> <!-- Disable CloudFlare email obfuscation -->
<label for="customer_user">
<?php
- esc_html_e( 'Customer:', 'woocommerce' );
+ esc_html_e( 'Customer', 'woocommerce' );
if ( $order->get_user_id( 'edit' ) ) {
$args = array(
'post_status' => 'all',
@@ -435,7 +435,7 @@ class WC_Meta_Box_Order_Data {
if ( $order->get_formatted_billing_address() ) {
echo '<p>' . wp_kses( $order->get_formatted_billing_address(), array( 'br' => array() ) ) . '</p>';
} else {
- echo '<p class="none_set"><strong>' . esc_html__( 'Address:', 'woocommerce' ) . '</strong> ' . esc_html__( 'No billing address set.', 'woocommerce' ) . '</p>';
+ echo '<p class="none_set">' . esc_html__( 'No billing address set.', 'woocommerce' ) . '</p>';
}
$billing_fields = self::get_billing_fields( $order, 'view' );
@@ -458,7 +458,10 @@ class WC_Meta_Box_Order_Data {
if ( 'billing_phone' === $field_name ) {
$field_value = wc_make_phone_clickable( $field_value );
} elseif ( 'billing_email' === $field_name ) {
- $field_value = '<a href="' . esc_url( 'mailto:' . $field_value ) . '">' . $field_value . '</a>';
+ $normalized_email = is_string( $field_value ) ? trim( $field_value ) : '';
+ $field_value = '' === $normalized_email
+ ? '<span class="none_set">' . esc_html__( 'No email address set.', 'woocommerce' ) . '</span>'
+ : '<a href="' . esc_url( 'mailto:' . $normalized_email ) . '">' . esc_html( $normalized_email ) . '</a>';
} else {
$field_value = make_clickable( esc_html( $field_value ) );
}
@@ -571,7 +574,7 @@ class WC_Meta_Box_Order_Data {
if ( $order->get_formatted_shipping_address() ) {
echo '<p>' . wp_kses( $order->get_formatted_shipping_address(), array( 'br' => array() ) ) . '</p>';
} else {
- echo '<p class="none_set"><strong>' . esc_html__( 'Address:', 'woocommerce' ) . '</strong> ' . esc_html__( 'No shipping address set.', 'woocommerce' ) . '</p>';
+ echo '<p class="none_set">' . esc_html__( 'No shipping address set.', 'woocommerce' ) . '</p>';
}
$shipping_fields = self::get_shipping_fields( $order, 'view' );