Re: Tabbed Interfaces in CSS

2009/4/20 Tab Atkins Jr. <jackalmage@gmail.com>:
> Greetings, list.
>
> [...]
>
>
> Problems in Advanced Layout Module tabbed layout
> ================================================
> The draft is no longer up, so I'll summarize how the proposal worked.
>
> display:card elements were placed into a display:stack container,
> defining their stack explicitly in the markup.  An element within the
> card was given display:tab.  This causes it to be pulled out of the
> card's display, and used to fill the automatically generated tab
> display docked on one side of the stack.
>
> There are several weaknesses with this draft:
>
> 1. It was a slight abuse of display (display:stack and display:card
> elements were treated as display:block by default).

Not really. It was display-model:stack (this element, while being
whatever, block/inline/list-item/table-cell, displays only one of its
children) and display-role:card (this element, while displaying its
content as whatever, block-inside/math/svg/table, takes the role of a
card in respect to his parent).

> 2. It forced the designer to put the card's stack relationship
> explicitly in the markup.  In many basic tabbed displays this is fine,
> but there can be legitimate reasons to *not* do this.

Not really, again. You're not required to use display:tab, as UAs are
still required to show cards that matches the :target pseudo-class.
You just need an hash-link.

> 3. The tabs were extremely limited.  They must come from the card's
> descendants, they must be next to the card stack, and their display is
> essentially browser chrome, limiting the author's ability to change
> the style of the tabs.

If you want to pull tabs out of the card, you're limited (not really,
once more, because you can reset the "appearance" and set what you
want on the tab element). If you don't want to use display:tab, you
don't need. The advantage of this is that you don't need hash-links.

> 4. Tabs and cards are forced into a one-to-one relationship,
> preventing an author from creating complex layouts like I outlined
> above in my example.

No, unless you want the default, native-looking and link-free, tab
layout. You can have multiple links with the same href.

> 5. Accordions are completely impossible, even though they are
> functionally identical to a tabbed layout.

Accordions are different, because they don't need tabs, just cards.
And stacked cards are achievable with current CSS support,tweaking a
bit the :target, for example with the following markup (designed to
degrade)
<section id="tab-1"><h1><a href="#tab-1">Tab
Title</a></h1><p>content</p></section>
you can have an accordion using
section:not(:target) > :not(h1:first-child) { display:none; }

(laying out appropriately the section, for example with Flexible Box)

> These weaknesses are largely a result of defensible choices, of
> course.  This approach makes it easy to take existing markup and
> transform it into a tabbed layout.  However, it severely restricts an
> author's options when they know that a tabbed layout is intended from
> the beginning.  Both this and my proposal (below) offer good fallback
> behavior in the absence of CSS.
>
>
> A Proposal for a Tabbed Layout
> ==============================
> My proposal takes the flexibility of JS-based tabbed layouts and
> marries it to the simplicity and rigor that CSS can bring.  It
> requires only a single new property and a handful of pseudoclasses.
> No pseudoelements or new values for existing properties are needed.
> It revolves around the use of hash-links and ids, but without leaning
> on the :target pseudoclass and with significant intelligence to help
> in styling.
>
> New Property: card-stack
> ------------------------
> Values: none | <string>
> Initial: none
> Applies to: all elements
> Inherited: no
>
> The card-stack property identifies an element as a card in a tabbed
> layout, and defines the stack it belongs to.  Only a single card (the
> 'active' card) from a given stack is displayed at a time - the rest of
> the cards in the stack are treated as display:none.

I don't really believe we need card-stack, we are fine with
display-model:stack and display-role:card. If you want, you can still
move content in the stack element (XSL-T or Named Flows)

> A card is made 'active' by either it or one of its descendants being
> expressly targetted by a hash-link.  Once a card is 'active', it
> remains 'active' until another card in the same stack is made
> 'active'.  Cards in other stacks can be made 'active' without
> affecting cards in any other stack.

Ok, but in a different way: "navigating to a different fragment
identifier should not change the currently active card in a different
stack". And I don't like that descendants can raise the card, I prefer
sticking with the card element itself.

> Any card with a string value for card-stack matches the pseudoclass
> :card.  Currently active cards also match the :active-card
> pseudoclass.  ((Is an :inactive-card pseudoclass desirable?  It's not
> necessary, but it may make things slightly easier - you won't have to
> set inactive properties with :card and then explicitly override them
> with :active-card.  It would also make defining the UA defaults easier
> - just say ":inactive-card{display:none !important;}".  Alternately,
> drop :card and just use :active-card and :inactive-card.))

I prefer :card and :active:card (that is, both :active and :card
pseudoclasses). :inactive-card is the same as :card:not(:active)

> Any link which is targetting a card on the page is considered a 'tab',
> and will match the :tab pseudoclass.  A link targetting a currently
> active tab will also match the :active-tab pseudoclass.  ((Again, do
> we want an :inactive-tab pseudoclass instead of/in addition to the
> :tab pseudoclass?))  Links targetting descendants of a card will
> activate the card when followed, but do not count as tabs themselves.

Again, I'd rather :tab , :active:tab and :not(:active):tab, although a
link is still a link (and :active has a different meaning for links),
so :active-tab may be necessary.

>
> Questions/Issues
> ----------------
> Currently, *all* cards start out inactive when you enter a page
> (unless the page had a hash, of course).  Most JS-based tabbed layouts
> will automatically activate the first card (with 'first' defined in
> some consistent manner).  Should we adopt this?  Should there be a
> control for this?  How would it work?

I think that the first card child, in css content order, inside the
stack, should be active unless the user interacts in other ways.

> Once a card has been activated from a stack, there's no way currently
> to close all cards from that stack - you'll always have one open.
> Most js-based tabbed layouts allow you to click an active tab to close
> the card.  This seems reasonable to me, but it would require a slight
> complexification of the card handling.

I don't know, but it does not seem more complex to me: you have to
track the currently active card for the stack, you can notice that
you're reactivating the same card.
This works only if you define the card handling imperatively (a set of
processing on the navigate action) instead of declaratively (a set of
states), something new to CSS but that does not really affect the
author, only the implementation.

> Should I expand the wording concerning activation and tab-selection to
> allow other means of targetting (such as XPointer)?  I don't see any
> particular reason *not* to.

You could say that the same algorithm used to resolve :target should
be used for associating (explicit) tabs with cards, so if an UAs
implements XPointer for target it will implement that also for Tabbed
Layout. You need to say that only the first matching element is
considered.
I expect that if Tabbed Layout will rely so much on fragment
identifiers, UAs will consider adding XPointer to avoid having an ID
for each tab.
Lastly, for what concerns activation and autoscrolling, a new property
in the Hyperlink Presentation Module (like target:no-change) may be
fine.

> I still liked that the original draft allowed you to easily transform
> existing markup into a tabbed layout.  Can anyone see an easy way to
> merge the two solutions?

Display:card inside a display:stack, and tabs with hyperlinks.

> ~TJ
>
>

Giovanni

Received on Monday, 20 April 2009 16:05:48 UTC