openui / open-ui

Maintain an open standard for UI and promote its adherence and adoption.
https://open-ui.org
Other
3.59k stars 191 forks source link

Consider <time> improvements #1023

Open jyasskin opened 8 months ago

jyasskin commented 8 months ago

The <time> element has its datetime attribute that expresses a point in time or a duration, but it doesn't use that to improve the information the user sees. https://github.com/github/relative-time-element and https://shoelace.style/components/relative-time have a group of behaviors that improve on this, and I think we should consider importing them into HTML.

I see 4 kinds of behaviors that might be useful:

Auto-formatting durations like <time datetime="P3DT7H">

Neither library handles this, and the only reason to do it on the client would be to auto-match the user's locale, which is also available through Accept-Language on the server. I'd call this low priority.

Auto-formatting points in time like <time datetime="2024-03-28T10:57:32Z">

Available through <relative-time datetime="2024-04-01T16:30:00Z" format="datetime"> but not available in <sl-relative-time>.

The benefit to this is in matching the user's timezone, which is not sent to the server by default. <relative-time> also omits the year if it's the current year.

The behavior here is nearly all defined by Intl.DateTimeFormat, except that the behavior of dropping the year is custom. That behavior resembles the way formatRange can drop part of the second date if it repeats part of the first date, except formatRange doesn't currently drop the year (at least in en-US), and it can drop the whole date part.

Representing points-in-time as offsets from now

This has 2 sub-options:

Use terms like "tomorrow"

This is the default behavior of both libraries, as <relative-time datetime="2024-04-01T16:30:00Z"> or <sl-relative-time date="2020-07-15T09:17:00-04:00">, and they show strings like "now", "2 minutes ago", "yesterday", or "3 days from now". <relative-time> updates that offset in real-time while <sl-relative-time> only does so if the sync attribute is given.

The output is produced using Intl.RelativeTimeFormat, but the conversion from a duration to the arguments of RelativeTimeFormat.format() (in particular, when to jump to the next unit) isn't standardized.

Show just the duration

Available through <relative-time datetime="2024-04-01T16:30:00Z" format="duration"> but not available in <sl-relative-time>.

There's a Stage 3 proposal for the rendering of this at https://github.com/tc39/proposal-intl-duration-format.


@keithamus maintains <relative-time> and probably has opinions. :)

keithamus commented 8 months ago

@keithamus maintains and probably has opinions. :)

Many 😉

Auto-formatting durations like <time datetime="P3DT7H">

Formatting durations is very useful but in general I think using an ISO duration can be a little unwieldy. I believe a more natural API for developers retrieving dates will likely be getting 2 dates from their DB, or comparing a date to "now" (which must be allowed to be an implicit value rather than a fixed date to allow for effective page caching). So e.g. <time datatime="now" datetimeto="{{future_date}}">. Formatting duration is, IMO, the core functionality that <relative-time> offers, but it always assumes the offset to be now - it would quite straightforward to expose customization of now but we have no need for it at GitHub (yet).

Auto-formatting points in time like <time datetime="2024-03-28T10:57:32Z">

In <relative-time> we've been slowly teasing apart the "old" functionality to get it to behave exactly like Intl.DateTimeFormat, but as you rightly point out the year is special. I wonder if TC39 would be interested in exploring adding an option to DateTimeFormat; specifying the year only if it's different?

The output is produced using Intl.RelativeTimeFormat, but the conversion from a duration to the arguments of RelativeTimeFormat.format() (in particular, when to jump to the next unit) isn't standardized.

FWIW the main issue here is that RelativeTimeFormat expects a single number + enum of the unit. Consequently we have custom logic to handle this. I've been slowly teasing this apart to try and steer toward using Temporal.Duration as that looks like it has the ability to round durations. My hope is that between Temporal.Duration + Intl.RelativeTimeFormat, the platform will do the majority of the heavy lifting.

Other notable things:

We have quite a bit of guidance in the primer docs around relative time. It's worth a read, in my opinion.

Thresholds

We have an option for threshold (which does take an ISO duration). Date deltas beyond this threshold will not render in relative format, and instead the localised date. This is important because relative dates are an lossy approximation for brevity, and only applicable where that loss is acceptable. For many dates (for example "last session activity" in the security page) we set the threshold to "P0" so it's never relative, while others might be "PT1M", so it says "just now" up until a minute but then provides the full date (for example ssh key creation). Many others are the default "P30D" - dates are rendered after a month.

"Live" Timers

<relative-time> produces live timers, that dynamically change on the page; if you see a comment just posted it'll say "now", then start counting from "10s" to "a minute ago", then continues to count up. Aside from media and JS interaction this would be quite a novel element as a built-in. Pages can have 100s of these live timers so we did put some work to make sure they're only updating when they have to.

Elapsed Timers

We also offer an elapsed time which counts up from the provided datetime. This is used in (for example) GitHub actions to show how long a job has been running. The way this is done is that the element is given a datetime of the job start time, and the element handles the rest. A very convenient API and a natural extension.

Accessibility

Accessibility for these times is... problematic. IIRC ATs don't do anything special with the <time> element. We add a title showing the full datetime which helps but falls short of a great experience for AT users. We used to render some relative times in a micro format, e.g. 1s instead of 1 second ago, however ATs will assume the unit suffix which means 1m is always read as 1 meter! We moved away from this format due to that.

github-actions[bot] commented 2 months ago

There hasn't been any discussion on this issue for a while, so we're marking it as stale. If you choose to kick off the discussion again, we'll remove the 'stale' label.