Re: Tabbed Interfaces in CSS

2009/4/20 Tab Atkins Jr. <jackalmage@gmail.com>:
> On Mon, Apr 20, 2009 at 11:05 AM, Giovanni Campagna
> <scampa.giovanni@gmail.com> wrote:
>> 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).
>
> I see no reason why an element couldn't be both a card and a table.
> Using display in this manner (even with the separation of role and
> model) forces you into using container elements if you do anything
> nontrivial.

And what is the problem? Anonymous boxes are still there, if you can
have a table from a bunch of table-cells, you can perfectly have a
stack from a sequence of cards.

>>> 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.
>
> ?_?  That statement said nothing about tabs.  It was about cards and stacks.

Sorry, I misunderstood. I thought it was about cards and tabs (the
fact that a tab has to be inside its own card). I'm against pulling
out cards from their stack.

>>> 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.
>
> If you don't use display:tab, you can't indicate which card is
> currently active.  The hash-links are stateless in this respect.

Your proposal does not use display:tab, uses hash-links. I meant that
both hash-links and explicit tabs (display:tab) can be used and they
have the same effect on the cards (they match :tab and :active-tab,
they don't affect history, they don't scroll, etc.)

>>> 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.
>
> That's what I meant.  Hash-links don't respond to the state of the
> card they're activating.  You have to use display:tab to get that.

Again, in your proposal, hask-links match the appropriate :active-tab
pseudo-class. The difference with display:tab is that the latter is
moved out from the card, into the tab bar, and you don't need to
restyle an <ul> to get the layout of Properties dialog box.

>>> 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.
>
> What?  Accordions certainly need tabs.  What do you think the things
> you click on to expand a given section are?  Are you perhaps thinking
> of something different than me?  I'm talking about markup like this:
>
> <div class="accordion">
>  <h1>Section one</h1>
>  <p>foo</p>
>  <h1>Section two</h1>
>  <p>bar</p>
> </div>
>
> Where "Section one" and "Section two" are always displayed, and you
> can click on them to show the "foo" or "bar" elements.

I was not sure of what accordions were and searched in wikipedia.
Maybe we are talking of different things

>> 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; }
>
> Indeed, that's perfectly possible, just as I illustrated that tabbed
> layouts are possible with current CSS.  However, you can only have a
> single accordion per page (to be precise, all the accordions will act
> like one big distributed accordion) and if you try to use a hash-link
> for anything else on the page it will close the accordion.

Agreed.

> In other words, it suffers from the exact same problems that I
> outlined for my pure-CSS tabbed display.  In trivial cases it's
> more-or-less usable, but it's not robust.
>
>>> 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)
>
> My entire argument is that we *aren't* fine with the old display:stack
> and display:card.  They're too limiting, sort of hacky, and require
> too much magic.
>
> XSLT transforms the document *before* CSS gets to it (you may be
> thinking of XBL2).  Named Flows, while still a pet project, is
> currently nothing more than a dream.  I think this can be defined well
> without it.

No, I am thinking of XSLT: all this discussions are because we cannot
change our markup to include a non semantic <div> wrapping all cards.
More correctly, we cannot include it in our pages, but we may include
it in our DOMs (with XBL, XSLT, Generated Content...)

>>> 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".
>
> That's a good way to phrase it.
>
>> And I don't like that descendants can raise the card, I prefer
>> sticking with the card element itself.
>
> Uh, why?  If you target an element, you expect the display to scroll
> to it.  Inactive tabs aren't explicitly hidden, just sort of
> 'unavailable' at the moment.

I'm sure it will make problems with the implementation.

>>> 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)
>
> Doesn't work.  It means that a card is only active *while you are
> manipulating elements inside of it*.  It would be impossible to use
> multiple stacks of cards.  Hell, it would be impossible to have *one*
> stack of cards, with a card visible while you're interacting with the
> non-tabbed part of the page.

I don't see what you mean. Yes, a card is :active when it is visible
and available to the user. You can have :enabled if you want.

>>> 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.
>
> Doesn't work, for the same reasons.  It actually *extra* doesn't work
> here, as the active tab is *nothing* like the semantics of :active.
> :active-tab is a tab associated with an active card; it has nothing to
> do with whether you are currently manipulating the tab.  (You would
> use :tab:active to change the display of a tab while it's being
> clicked.)

Agreed, :active-tab is necessary (also because tabs are expected to be
push-buttons that change while pressed)

>>> 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.
>
> In CSS content order?  Do you mean DOM order?  I'd agree with that.

DOM order with Generated Content, Named Flows, anonymous box
generation, whitespace handling, bidi reordering, etc.

> My proposal doesn't have a stack that something can be inside of.  A
> stack in my proposal is just a unique identifier that cards associate
> themselves with.  Are you talking about the older proposal?

Yeah, I never abandoned display-model:stack.

>>> 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.
>
> Yeah, it's not much more complex, it's just that the language has to
> change.  I already require hidden states (card *activation* is
> determined by the hash, but a card *staying* active is based on hidden
> history tracked by the CSS engine), it would just require slightly
> more here.
>
>>> 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.
>
> Sounds good.
>
>> 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.
>
> Hmm, that would be nice.  Something to poke at for the future.
>
>> Lastly, for what concerns activation and autoscrolling, a new property
>> in the Hyperlink Presentation Module (like target:no-change) may be
>> fine.
>
> Yeah, current hash-link behavior isn't ideal for tabs.  It's workable,
> but could be better.
>
>>> 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.
>
> That doesn't do it.  If we have to add links to the document for tab
> activation, we might as well #id the cards as well and just use this
> proposal.  I'm talking about how the original proposal could take a
> document that was *completely* not intended for tabbed display, and
> still handle it well (assuming the document met certain structural
> requirements, namely that all the desired cards were siblings).

I never really abandoned display:tab, so you just need
body { display-model:stack; }
body > section { display-role:card; }
body > section > h1 { display-role:tab; }

And instead of continuous flow, you get multiple tabs. You only need
hyperlinks when tabs are outside of their card

> ~TJ
>

2009/4/20 Brad Kemper <brad.kemper@gmail.com>:
>
> On Apr 20, 2009, at 9:51 AM, Tab Atkins Jr. wrote:
>
>> However, you can only have a
>> single accordion per page (to be precise, all the accordions will act
>> like one big distributed accordion) and if you try to use a hash-link
>> for anything else on the page it will close the accordion.
>>
>> In other words, it suffers from the exact same problems that I
>> outlined for my pure-CSS tabbed display.
>
> With my pure-CSS tabbed display, you could have more than one set of tabs
> per page.
>

It is not pure-CSS, there are radio buttons. Unless you could add
those radio buttons dynamically (which is not possible because
appearance is not supported and binding is non standard), that
solution is out of scope.

Giovanni

Received on Monday, 20 April 2009 22:51:20 UTC