Make WordPress Core

Opened 2 years ago

Last modified 3 days ago

#61652 reopened enhancement

Use `input type="datetime-local"` as a progressive enhancement for classic editor scheduling

Reported by: edent Owned by:
Priority: normal Milestone:
Component: Date/Time Version: 6.5.5
Severity: normal Keywords: 2nd-opinion close has-patch
Cc: Focuses: ui, accessibility, administration

Description

When scheduling a blog post to be published later, user have to use this WordPress control:

https://shkspr.mobi/blog/wp-content/uploads/2023/09/WP-Date-Picker.png

I find it mildly annoying. I don't get why part of it is a dropdown. And the number fields don't pop up my phone's number keypad. And I have to look at a different calendar if I want to schedule something for a Saturday.

The back end code for validating the POST'd timestamp is complex and inefficient - https://github.com/WordPress/wordpress-develop/blob/29c2f0154c180ecf92448e0f3f4e6430745c868b/src/wp-admin/includes/post.php#L178

Replacing the various <input>s with <input type="datetime-local"> makes the front end much simpler. It works on all browsers and is easier to use on mobile devices.

When the element is submitted, it POSTs an ISO8601 / RFC 3339 string. Something like 2023-08-27T12:34

This makes the back end much simpler as well. The validation is either:

$dateTime = DateTime::createFromFormat( "Y-m-d\TH:i", $post_data['dateTime'] );
if ( $dateTime == false ) {
   return new WP_Error( 'invalid_date', __( 'Invalid date.' ) );
}

or

$dateTime = $post_data['dateTime'];
if ( strtotime($dateTime) == false ) {
   return new WP_Error( 'invalid_date', __( 'Invalid date.' ) );
}

Based on my very rough profiling, I expect the new function to be about twice as fast, albeit using about twice the memory. But we're talking fractions of seconds and hundreds of bytes.

There's a little more discussion on my blog post - https://shkspr.mobi/blog/2023/09/should-the-wordpress-publish-scheduler-use-datetime-local/

Attachments (5)

current-classic-editor-timestamp-fields.png (153.1 KB ) - added by poligilad 8 weeks ago.
datetime-local-core-prototype.png (1.2 MB ) - added by poligilad 8 weeks ago.
woocommerce-product-editor-datetime-local.png (149.8 KB ) - added by poligilad 8 weeks ago.
split-date-time-alternative.png (133.5 KB ) - added by poligilad 8 weeks ago.
Publish box - date & time - core tracs.png (147.1 KB ) - added by poligilad 7 weeks ago.

Download all attachments as: .zip

Change History (27)

#1 @Otto42
2 years ago

  • Keywords needs-patch added

I like it. +1

#2 @swissspidy
2 years ago

  • Focuses performance removed
  • Keywords 2nd-opinion added

There is a lack of browser support for datetime-local. Both Firefox and Safari only display a date picker and do not display a time picker.

The referenced blog post mentions only the classic editor, which is not a priority compared to the block editor.

Removing performance focus as this is a micro optimization in a cold path at best.

#3 @edent
2 years ago

  • Focuses performance added

There is a lack of browser support for datetime-local. Both Firefox and Safari only display a date picker and do not display a time picker.

That is not a worse situation than we currently have. The fallback - plain text entry - provides an identical user experience to the the existing input. On FF mobile, and other platforms, the UI is greatly improved.

The referenced blog post mentions only the classic editor, which is not a priority compared to the block editor.

There are over 10 million people still using the Classic Editor: https://wordpress.org/plugins/classic-editor/

We'd like a little love and attention too 😊

Removing performance focus as this is a micro optimization in a cold path at best.

I was inspired by this blog post which talks about small optimisations - https://wordpress.com/blog/2023/08/24/speedier-php-execution-in-wordpress-6-3/ - so I've re-added the focus.

The Gutenberg scheduler POSTs its date as an RFC3339 string. I assume that was done for performance and maintainability reasons. Personally, I'd like to see the Gutenberg scheduler replaced with a native HTML control as well.

I'll contribute a patch later this week - does anyone know if any other components use _wp_translate_postdata()?

#4 @swissspidy
2 years ago

  • Component Date/TimeEditor
  • Focuses performance removed

This ticket was mentioned in Slack in #accessibility by joedolson. View the logs.


2 years ago

#6 @joedolson
2 years ago

The native datetime inputs have extremely mixed accessibility support, so in order for this to be considered a viable path for accessibility we'd need to fully test the accessibility in every browser and compare that to the accessibility of the existing settings.

The last major round of accessibility testing I'm aware of for the datetime inputs was a few years ago, so things may have changed since then; but they were notably problematic for voice command input at that time.

We could make some minor changes to this control to support things like the number pad on mobile devices, but I agree with @swissspidy that major changes to the classic editor interface are not a priority.

This ticket was mentioned in Slack in #accessibility by joedolson. View the logs.


2 years ago

#8 @joedolson
2 years ago

  • Keywords close added

After discussion in the accessibility bug scrub, we're going to recommend closing this. Given that we don't need to spend a lot of time on classic editor changes, and the accessibility concerns in changing to native inputs in this case, it doesn't seem like a valuable use of time.

Additionally, this would be potentially burdensome for documentation and support issues, since the interface would vary between browsers.

#9 @sabernhardt
2 years ago

  • Keywords has-patch added; needs-patch removed

The referenced blog post mentions only the classic editor

Quick Edit and Comments also use the same set of touch_time() fields. If browser and assistive technology support for datetime-local is good enough (in the future), I think that creating a new function could be better than editing touch_time().

We could make some minor changes to this control to support things like the number pad on mobile devices

I would like to add the inputmode attribute as a simple improvement now. I uploaded a patch to this ticket, but if closing as 'maybe later' makes more sense, my patch could move to a separate ticket.

#10 @sabernhardt
23 months ago

  • Component EditorDate/Time
  • Focuses administration added
  • Keywords has-patch removed

I deleted my patch from this ticket and opened #62109 to improve the current fields a little with the number pad.

Replacing the set of fields was also considered on #43028 (with the datepicker from Gutenberg).

This ticket was mentioned in Slack in #accessibility by joedolson. View the logs.


22 months ago

This ticket was mentioned in Slack in #accessibility by joedolson. View the logs.


21 months ago

#13 @joedolson
21 months ago

  • Milestone Awaiting Review
  • Resolutionmaybelater
  • Status newclosed

I'm going to close this as a maybelater, in hopes that at some point the standard date time input fields are good enough for general use. But for right now, it's not really a feasible change; the improvements from @sabernhardt help make the mobile experience a bit better, at least.

#14 @poligilad
8 weeks ago

  • Resolution maybelater
  • Status closedreopened
  • Summary Use `<input type="datetime-local">` for scheduling - performance and usability enhancementUse `input type="datetime-local"` as a progressive enhancement for classic editor scheduling

Reopening with additional testing and a narrower progressive-enhancement proposal.

I’ve been exploring this again while working on a related WooCommerce product editor improvement: https://github.com/woocommerce/woocommerce/pull/65826

The earlier concerns around browser UI differences and accessibility are valid, so I tested two possible approaches locally in the classic editor:

  1. A single datetime-local input.
  2. Separate native date and time inputs.

The split date/time version initially seemed attractive because it avoids relying on a combined browser picker. In practice, it does not fit the classic Publish metabox very well. The native icons and narrow available width make the two controls visually awkward, and the time field can become cramped depending on browser/font sizing.

The single datetime-local input is the cleaner fit for the existing UI. It preserves the compact one-line layout, is easier to scan, and avoids adding more visual complexity to the Publish box.

This proposal is intentionally narrower than replacing the backend timestamp handling. The existing touch_time() fields can remain the source of truth and fallback. The native input can progressively enhance the classic editor only when datetime-local is supported, syncing back to the existing aa, mm, jj, hh, and mn fields. That keeps the change smaller and avoids changing the submitted data shape.

I also tested VoiceOver in Chromium-based Brave and Safari. In both browsers, the field was announced as a date/time edit control, and the individual date and time segments were available for keyboard editing.

Safari still has an important UI limitation: clicking the picker icon appears to show only the date picker, not a combined date and time picker. However, the time portion is still present in the field and remains editable by focusing the control and typing or adjusting the time segment directly. So Safari’s picker UI is less complete than Chromium’s, but the field itself still supports editing both date and time.

That matches the earlier concern that native UI varies by browser. The narrower proposal here is not that the native UI is identical everywhere, but that this can be used as progressive enhancement while keeping the existing touch_time() fields as the fallback/source of truth.

I think this is worth reopening because the proposal is now smaller than the original ticket description: it does not require replacing the backend timestamp handling or changing the submitted data shape, and it keeps the existing fields as the fallback/source of truth. The goal would be a progressive enhancement for browsers and assistive technology combinations where the native control is an improvement, while preserving the current behavior where it is not.

I’m attaching screenshots of:

  • the current classic editor control,
  • the single datetime-local prototype,
  • the split date/time alternative tested but not preferred,
  • the related WooCommerce product editor use case.

This ticket was mentioned in PR #12275 on WordPress/wordpress-develop by @poligilad.


8 weeks ago
#15

  • Keywords has-patch added

## Summary

  • Adds a datetime-local progressive enhancement for the classic editor publish timestamp field when the browser supports the native control.
  • Keeps the existing touch_time() fields as the source of truth and fallback, syncing the native value back to aa, mm, jj, hh, and mn.
  • Applies the enhanced field styling in the Publish metabox without changing the submitted data shape.

## Testing

  • Opened the classic post editor locally and verified the Publish timestamp field renders as a single native date/time input.
  • Verified changing the native input updates the existing legacy timestamp fields.
  • Verified an invalid native value keeps the timestamp editor open and marks the field invalid.
  • Verified the enhanced field appears in the WooCommerce classic product editor because products use the same classic editor Publish metabox.
  • Ran node .context/classic-datetime-smoke.mjs.
  • Ran npx grunt jshint:core.

#16 @tyxla
7 weeks ago

Thanks for putting this together, @poligilad. Before we go further on code, though, I think a few design questions need to be settled here on the ticket, because the current approach quietly changes some long-standing behavior. I wasn't in the prior discussion, so I welcome folks with more context to share thoughts and opinions:

  1. Locale and timezone - datetime-local renders in the browser's locale and 12/24h preference and exposes no timezone. granular aa/mm/jj/hh/mn fields were chosen deliberately because they're locale-independent and show the site's wall-clock time predictably, independent of the browser. By hiding the granular fields and showing only the native control, we hand presentation to the browser and disconnect it from the site's date_format / time_format / timezone settings. How do we want to reconcile that — is browser-locale presentation acceptable here, or does the native field need to coexist with the existing fields rather than replace them?
  1. Accessibility - from what I understand, datetime-local has uneven screen-reader and keyboard support across browsers/AT, and this patch hides the granular fields entirely with no way to switch back. For a core admin screen, that's a meaningful regression risk. What's your take on it, and can we get broader feedback from folks who have participated in this ticket already?
  1. The split date/time alternative - The PR screenshots include a split date/time variant. Given (1) and (2), that may be the safer path — it's a smaller behavioral change and avoids the single-control locale/timezone surprises. May be useful to get broader feedback before iterating on the patch?
  1. Where the field lives - Independent of the above: if this proceeds, the new field should be rendered server-side in touch_time() and merely activated by JS, rather than constructed as an HTML string in post.js. That keeps it consistent with how the rest of this UI (and its i18n/escaping) is built.

My suggestion would be to not replace the granular fields, and to scope the first pass to whichever of the two presentations (single native vs. split) we're comfortable shipping with full review.

I'm also leaving some code specific feedback in the PR.

#17 @poligilad
7 weeks ago

Thanks for the review, @tyxla 🙏

I updated the PR direction based on this feedback. The latest approach no longer uses a single datetime-local field. Instead, it uses separate native date and time inputs, stacked vertically in the Publish metabox.

I think this helps reduce the main concerns raised here:

  • Locale/time display: splitting date and time avoids some of the ambiguity of a combined datetime-local control. The UI now also includes an explicit “Site time” note below the fields, similar to the pattern in the Gutenberg editor UI, so it is clearer which timezone the scheduled/published time refers to.
  • Accessibility/browser behavior: the split controls rely on simpler native input types and avoid depending on a combined browser date/time picker, which was especially uneven in Safari.
  • Layout: stacking the date and time fields one below the other works much better in the narrow Publish metabox than putting them side by side. It also avoids the cramped time input issue from the earlier split-field prototype.
  • Scope: the existing granular aa, mm, jj, hh, and mn fields remain the source of truth/fallback. The native fields progressively enhance the UI and sync back to the existing fields, so the submitted data shape is unchanged.
  • Rendering: the native fields are now rendered server-side in touch_time(), with JS only activating/syncing them when supported.

I pushed the updated implementation to the PR and will also respond to the code-specific comments there.

@poligilad commented on PR #12275:


7 weeks ago
#18

Thanks for the review, @tyxla.

I updated the PR based on the ticket discussion and this feedback.

The approach no longer uses a single datetime-local input. It now uses separate native date and time inputs, stacked vertically in the Publish metabox.

I think this addresses the main concerns more directly:

  • It avoids relying on a combined browser date/time picker.
  • It keeps the date and time easier to understand as separate values.
  • It fits the narrow Publish metabox better now that the fields are stacked.
  • It adds an explicit “Site time” note below the fields, which is closer to the newer editor/product editor pattern and clarifies which timezone the value represents.
  • The existing aa, mm, jj, hh, and mn fields remain the source of truth/fallback, so the submitted data shape is unchanged.
  • The native fields are now rendered server-side in touch_time(), with JS only activating and syncing them when supported.

I’ll reply to the code-specific comments inline as well.

This ticket was mentioned in PR #13050 on WordPress/wordpress-develop by @jillq.


3 days ago
#19

## Summary

Continues the work in #12275 (opened by @poligilad-auto, who is currently out) to enhance the classic editor Publish timestamp with server-rendered native date and time controls, keeping the existing granular aa/mm/jj/hh/mn fields as the source of truth and fallback.

This branch carries Poli's original commit unchanged and adds a follow-up commit addressing @tyxla's code review:

  • Removed a duplicated CSS rule. .misc-pub-curtime #timestamp:before { content: none } was declared twice; kept the later declaration so it still overrides .curtime #timestamp:before (equal specificity, so source order decides which wins).
  • Consistent invalid-state selectors. The form-invalid class is now added and removed on both native inputs via .timestamp-native-wrap input, rather than being added to only #publish-date-native by ID while being removed from both.
  • UTC instead of UTC+0. A zero timezone offset now renders as UTC in the site-time note; non-zero offsets are unchanged (UTC+5:30, etc.).
  • Aligned the timestamp row with its siblings. The Publish-box icon column used a fixed 27px, which pushed the timestamp label ~10px further right than the Status / Visibility / Revisions rows. Sizing the column with auto lets it match the sibling dashicon advance, so all rows line up. All new layout CSS remains scoped to .misc-pub-curtime, so the other meta boxes are unaffected.

## Still open from the review

The two higher-level points @tyxla raised on #12275 are not resolved here and still need discussion:

  • Confirming this is the design direction the project wants for the classic editor timestamp.
  • Confirming the markup changes don't break plugins that integrate with the existing controls.

## Testing

  • Opened the classic post editor and confirmed the Publish timestamp renders as native date and time inputs with the site-time note, and that the timestamp row aligns with the other Publish-box rows.
  • Confirmed changing the native inputs updates the legacy timestamp fields, and an invalid value keeps the editor open and marks both native inputs invalid.
  • php -l src/wp-admin/includes/template.php — no syntax errors.
  • Measured the Publish-box row alignment before and after the CSS change (sibling rows and the timestamp row now share the same 16.5px label indent).

The follow-up commit was drafted with AI assistance and reviewed by me.

Props poligilad, tyxla.

@jillq commented on PR #12275:


3 days ago
#20

I've opened #13050 as a draft, which carries Poli's commit unchanged and adds a follow-up commit addressing the code comments from @tyxla's last review.

  • Removed the duplicated .misc-pub-curtime #timestamp:before rule
  • Made the native inputs' invalid-state selectors symmetric
  • Rendered a zero timezone offset as UTC rather than UTC+0.
  • Fixed the Publish-box alignment: the timestamp row's icon column was a fixed 27px, which pushed its label ~10px right of the sibling rows — switching it to auto lines them all up, and the new CSS stays scoped to .misc-pub-curtime so the other meta boxes are untouched.

(Thanks Marin!)

@Joen commented on PR #13050:


3 days ago
#21

Nice, thanks so muc for contributing! From a quick glance it looks good to me, but honestly I'll also defer a bit to you and Poli on the design side. I understand this is motivated by good reasons, and thus I mostly want to support it with my own thumbs up.

The changeset looks reasonable to me, so I'm wondering: how can we help this ship? Let's see if some developers check in on this with input, otherwise if nothing happens, ping me again and we'll try and see if a @WordPress/wordpress-core ping can help.

@jillq commented on PR #13050:


3 days ago
#22

Thanks @jasmussen (and for the quick reply)! 🙏

@tyxla -- following up on your point about not breaking plugins that integrate with the existing controls. To investigate this, I was directed by Claude to run the change through <a href="https://wpdirectory.net" target="_blank" rel="noreferrer" class="text-accent hover:underline underline-offset-[1px] outline-none hide-focus-ring ring-focus rounded-r2">WPDirectory</a> (regex across all current plugins + themes on .org)</p><p dir="ltr"><strong>The results:</strong></p><div dir="ltr" class="overflow-x-auto">
Pattern | Plugins | Themes
-- | -- | --
misc-pub-curtime\s*> (CSS child selectors into the old structure) | 0 | 0
siblings\(…a\.edit-timestamp (the JS relationship) | ~14 plugins | 0
edit-timestamp-label\|timestamp-native-wrap\|timestamp-display (new class names) | 1 (unrelated plugin's own class) | 0

</div><p dir="ltr">So the CSS changes look safe, and no themes are affected. The one real compatibility surface is the JS: wrapping the display + Edit link moved <code data-epitaxy-inline-code="">a.edit-timestamp</code> out from being a sibling of <code data-epitaxy-inline-code="">#timestampdiv</code>, so <code data-epitaxy-inline-code="">$('#timestampdiv').siblings('a.edit-timestamp')</code> stops resolving.</p><p dir="ltr"><strong>Triaging the ~14 hits:</strong></p><ul dir="ltr"><li style=""><strong>Classic Editor (9M installs)</strong> looks alarming but is a false alarm — it ships a vendored copy of core's <code data-epitaxy-inline-code="">post.js</code> under <code data-epitaxy-inline-code="">scripts/</code>, but nothing in the plugin enqueues it (the classic editor runs on core's own <code data-epitaxy-inline-code="">post.js</code>). So the matched code doesn't execute. (TCD Classic Editor, 4k, vendors it the same way.)</li><li style="">The genuinely-at-risk set is a handful of status/scheduling plugins that enqueue their own script targeting core's <code data-epitaxy-inline-code="">#timestampdiv</code> — e.g. <strong>Media Library Assistant</strong> (70k, on the attachment screen), <strong>PublishPress Statuses</strong> (1k), <strong>LH Archived Post Status</strong> (4k), plus a few with tens of installs. Real, but niche.</li><li style="">One match (Depublish Posts) is a false positive — it uses its own <code data-epitaxy-inline-code="">edit-timestamp-depublish</code> class.

</li></ul><p dir="ltr"><strong>Mitigation which has been pushed:</strong> rather than accept even a small break, I kept <code data-epitaxy-inline-code="">#timestamp</code> and <code data-epitaxy-inline-code="">a.edit-timestamp</code> as direct children of <code data-epitaxy-inline-code="">.misc-pub-curtime</code> (so the sibling relationship is preserved) and laid the label row out with a three-column grid instead of the <code data-epitaxy-inline-code="">.timestamp-display</code> wrapper (from Poli's original PR).

The rendered result and alignment are unchanged; <code data-epitaxy-inline-code="">$('#timestampdiv').siblings('a.edit-timestamp')</code> resolves again. That takes the compatibility question off the table at no visual cost.</p><p dir="ltr">

Pls LMK if there's something else you think I could do to ensure backward compatibility here! 🙏

Note: See TracTickets for help on using tickets.