├── .gitignore ├── Gruntfile.js ├── README.md ├── fonts ├── FontAwesome.otf ├── fontawesome-webfont.eot ├── fontawesome-webfont.svg ├── fontawesome-webfont.ttf └── fontawesome-webfont.woff ├── img ├── basic-panel.png ├── collapsed-panel.png ├── dom-explained.png ├── removable-panel.png └── titled-panel.png ├── index.src.html ├── package.json ├── panelset-cpreuse.less ├── panelset-without-pd.css ├── preferrential-display.html ├── prototype ├── angular-panels.js ├── custom-elements.js ├── fill.js ├── mini.html ├── panels.js ├── panelset-directive.html ├── panelset-element-tabs.html ├── panelset-element.html └── panelset.css ├── test.html └── test ├── assertions.js ├── directive-tests.html └── standard-tests.html /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | node_modules 4 | build -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.event.on('qunit.spawn', function (url) { 3 | grunt.log.ok("Running test: " + url); 4 | }); 5 | // Project configuration. 6 | grunt.initConfig({ 7 | pkg: grunt.file.readJSON('package.json'), 8 | concat: { 9 | options: { 10 | banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' 11 | }, 12 | build: { 13 | files: [ 14 | { src: ['prototype/fill.js', 'prototype/panels.js'], dest: 'build/panels.min.js' }, 15 | { src: ['prototype/fill.js', 'prototype/angular-panels.js'], dest: 'build/angular-panels.min.js' } 16 | ] 17 | } 18 | }, 19 | qunit: { 20 | options: { 21 | timeout: 10000 22 | }, 23 | all: { 24 | options: { 25 | urls: [ 26 | 'http://localhost:8000/test/directive-tests.html', 27 | 'http://localhost:8000/test/standard-tests.html' 28 | ] 29 | } 30 | } 31 | }, 32 | connect: { 33 | server: { 34 | options: { 35 | port: 8000, 36 | base: '.' 37 | } 38 | } 39 | } 40 | }); 41 | 42 | // Load the plugin that provides the "uglify" task. 43 | grunt.loadNpmTasks('grunt-contrib-uglify'); 44 | grunt.loadNpmTasks('grunt-contrib-concat'); 45 | grunt.loadNpmTasks('grunt-contrib-connect'); 46 | grunt.loadNpmTasks('grunt-contrib-qunit'); 47 | 48 | // Default task(s). 49 | grunt.registerTask('default', ['concat', 'connect', 'qunit']); 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Common Panels and Panelsets HTML Extension Proposal 2 | Repo for https://bkardell.github.io/common-panel/index.src.html 3 | -------------------------------------------------------------------------------- /fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /img/basic-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/img/basic-panel.png -------------------------------------------------------------------------------- /img/collapsed-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/img/collapsed-panel.png -------------------------------------------------------------------------------- /img/dom-explained.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/img/dom-explained.png -------------------------------------------------------------------------------- /img/removable-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/img/removable-panel.png -------------------------------------------------------------------------------- /img/titled-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkardell/common-panel/4977b49843d3e9aa0e5b390dc6ad4bbf7b61b919/img/titled-panel.png -------------------------------------------------------------------------------- /index.src.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Panels and Panel Sets Extension 6 | 7 | 8 | 14 | 15 | 42 | 43 | 44 | 45 |
46 | 47 |

This specification extension defines elements and attributes for constructing a panel or collection of panels based on a single interaction paradigm.

48 | 49 |

Using custom elements, a collection of functional prototypes is available. All DOM methods are under-scored to preserve forwards compatibility.

50 |
51 | 52 |
53 |

Work on this extension is being done on Github. Please share comments and feedback using the Github issue tracker.

54 |
55 | 56 |
57 |

Introduction

58 |

The concept of a content panel originates in print media and has carried over into digital media. The transition to digital media brought about the ability to interact with collections of panels, and over time a number of visual metaphors for this interaction have emerged. Tabs, accordions, decks and carousels are all design patterns based on interactions with sets of content panels.

59 | 60 |

The history of interface design shows that each of these design patterns has evolved independently, despite their similarities. The WAI-ARIA Authoring Practices Guide for example, describes a set of tabs and an accordion widget in identical terms except for visual orientation.

61 | 62 |

With the growing number of screen sizes and the advent of responsive design, these visual metaphors may no longer be constant. A set of tabs may be the logical choice for displaying content on a wide-screen, but the same content may be more intuitively displayed as an accordion or a deck on a narrow-screen. However, the interaction model and semantic information for each of these design patterns remains the same, regardless of the visual representation.

63 | 64 |

This extension defines a set of HTML elements and attributes for representing collections of content panels with appropriate semantics and interactions. The task of adopting a particular visual metaphor is left to CSS.

65 |
66 | 67 |
68 |

Conformance

69 |

All authoring guidelines, diagrams, examples, notes, and sections marked "informative" in this specification are informational. Everything else in this specification is "normative" as described in [[!qaframe-spec]]. The key words "must", "should", "should not" and "may" in this specification are to be interpreted as described in [[!RFC2119]].

70 | 71 |
72 |

Dependencies and Definitions

73 |

The Interface Definition Language (IDL) fragments in this specification must be interpreted as required for conforming IDL fragments, as described in the Web IDL specification. [[!WEBIDL]]

74 | 75 |

This document uses the terms accessible and accessibility in the sense of ensuring people with disabilities can use the Web.

76 |
77 |
78 | 79 |
80 |

Use cases and Requirements

81 |

The panels specification describes a number of elements and attributes that give authors the ability to present panels or collections of panels in a semantically consistent way.

82 | 83 |
84 |

Use Cases for Panels and Panel Sets

85 | 86 |
87 |

Use cases for the tab set metaphor

88 |
    89 |
  • Displaying semester timetables for the school year on a college intranet.
  • 90 |
  • Displaying nutritional values, ingredients, allergy warnings etc. about a food product on a grocery store website.
  • 91 |
  • Displaying configuration settings, statistics, performance data etc. about a software deployment pipeline on a continuous build server.
  • 92 |
  • Displaying descriptions, details, floor plans etc. about a house for sale on a property website.
  • 93 |
  • Displaying alternative representations of data (graphical and tabular) in an analytics web application.
  • 94 |
95 |
96 | 97 |
98 |

Use cases for the accordion metaphor

99 |
    100 |
  • Displaying bulletin board messages on an office intranet.
  • 101 |
  • Displaying an A to Z of glossary definitions on a medical research website.
  • 102 |
  • Displaying answers to frequently asked questions for newbies on a gamer’s forum.
  • 103 |
104 |
105 | 106 |
107 |

Use cases for the carousel metaphor

108 |
    109 |
  • Displaying bulletin board messages on an office intranet.
  • 110 |
  • Displaying attractive pictures of a spa resort on a vacation brochure website.
  • 111 |
  • Advertising key features of a prototype flying car on the company’s corporate website.
  • 112 |
113 |
114 | 115 |
116 |

Use cases for the coverflow metaphor

117 |
    118 |
  • Flipping through an album of sky dive photos on a photo sharing website.
  • 119 |
  • Browsing a portfolio of watercolours on an artist’s brochure website.
  • 120 |
121 |
122 | 123 |
124 |

Use cases for the deck metaphor

125 |
    126 |
  • Displaying a slide deck from a conference talk.
  • 127 |
  • Storing contact information in a rolodex application.
  • 128 |
  • Viewing pictures of a spa resort's vacation brochure website on a small device.
  • 129 |
  • Flipping through an album on a photo sharing website.
  • 130 |
  • Browsing a portfolio of watercolours on an artist’s brochure website.
  • 131 |
  • Advertising key features of a prototype flying car on the company’s corporate website, for display on a small device.
  • 132 |
  • Presenting a series of comic panel mock-ups, each representing a significant moment in the overall narrative.
  • 133 |
134 |
135 |
136 | 137 |
138 |

Requirements for Panels and Panel Sets

139 |
140 |
Consistent
141 |
It must be possible to interact with a panel or set of panels in a consistent way, using different modes of interaction including keyboard, mouse, voice and touch.
142 |
Identifiable
143 |
It must be possible for a user agent to identify the components of a panel, and the component panels within a set.
144 |
Stylable
145 |
It must be possible to style a panel or set of panels to provide different visual affordances.
146 |
147 |
148 |
149 | 150 |
151 |

The panel element

152 |
153 |
Categories:
154 |
156 |
Flow content.
157 |
If the element is a child of a panelset element, or has the removable or expandable attributes: Interactive content. [unsure about this as the interactive element is the paneltitle]
158 |
Palpable content.
159 |
Contexts in which this element can be used:
160 |
Where flow content is expected.
161 |
Content model:
162 |
One paneltitle element followed by flow content.
163 |
Content attributes:
164 |
Global attributes.
165 | removable - hints that the panel can be removed from the interface.
166 | expandable - indicates the expanded state of the panel.
167 |
Tag omission in text/html:
168 |
Neither tag is omissable.
169 |
Allowed ARIA role attribute values:
170 |
group role (default - do not set)
171 |
Allowed ARIA state and property attributes:
172 |
Global ARIA attributes.
173 | Any ARIA attributes applicable to the allowed roles.
174 |
DOM interface:
175 |
Uses HTMLElement ??
176 |
177 | 178 |

A panel element represents a chunk of content with a header that includes a visible title (paneltitle element). A panel element may be used on its own, or with other panel elements as part of a collection (panelset element).

179 | 180 |

181 | Github issue 16
182 | It isn't presently possible to imperitively create a panel element.

183 | 184 |

185 | Github issue 9
186 | Should it be possible to script a panel element to become either removable or expandable?

187 | 188 |

189 | Github issue 32
190 | Is panel-title a required child of panel element? 191 |

192 |

193 | Github issue 33
194 | why not re-use open attribute instead of expandable? 195 |

196 |
197 | 198 |
199 |

The paneltitle element

200 |
201 |
Categories:
202 |
203 | 205 |
206 |
None.
207 |
Contexts in which this element can be used:
208 |
As the first or last child of a panel element.
209 |
Content model:
210 |
Flow content.
211 |
Content attributes:
212 |
Global attributes. 213 |
214 |
Tag omission in text/html:
215 |
Neither tag is omissable.
216 |
Allowed ARIA role attribute values:
217 |
group role (default - do not set) ??
218 |
Allowed ARIA state and property attributes:
219 |
Global ARIA attributes.
220 | Any ARIA attributes applicable to the allowed roles.
221 |
DOM interface:
222 |
Uses HTMLElement ??
223 |
224 | 225 |

A paneltitle element represents the visible title of a panel (panel element). A paneltitle is the basic component of the panel header.

226 | 227 |
228 |
panel element with paneltitle element
229 | panel element with paneltitle element 230 |

231 | <panel>
232 | <paneltitle>Favourite books</paneltitle>
233 | <p>Here is a list of my favourite books:</p>
234 | <ul>
235 | <li>Hitchhiker's Guide to the Galaxy</li>
236 | <li>Pride and Prejudice</li>
237 | <li>Green Eggs and Ham</li>
238 | </ul>
239 | </panel>
240 | 
241 |
242 |
243 | 244 |
245 |

The panelset element

246 |
247 |
Categories:
248 |
249 | 251 |
252 |
Flow content.
253 |
 
254 |
Contexts in which this element can be used:
255 |
Where flow content is expected.
256 |
Content model:
257 |
One or more panel elements.
258 |
Content attributes:
259 |
Global attributes. 260 |
261 |
Tag omission in text/html:
262 |
Neither tag is omissable.
263 |
Allowed ARIA role attribute values:
264 |
group role (default - do not set)
265 |
Allowed ARIA state and property attributes:
266 |
Global ARIA attributes.
267 | Any ARIA attributes applicable to the allowed roles.
268 |
DOM interface:
269 |
Uses HTMLElement ??
270 |
 
271 |
272 | 273 |

A panelset element represents a collection of panels (panel elements). A panelset may contain one or more panel elements.

274 | 275 |

The panelset element manages the selection of its child panel elements. In other words, when panel elements are children of a panelset element, they are automatically collapsible.

276 | 277 |

The first panel element within a panelset element should be expanded by default. Any subsequent claims of expansion trump the original claim.

278 | 279 |
280 |
panelset element containing multiple panel elements
281 |

282 | <panelset>
283 | <panel>
284 | <paneltitle>Tequila Chamucos</paneltitle>
285 | <p>Chamucos reposado tequila is preserved in white oak barrels for...</p>
286 | </panel>
287 | 
288 | <panel>
289 | <paneltitle>Gran Patron Platinum</paneltitle>
290 | <p>Gran Patron Platinum tequila is distilled three times...</p>
291 | </panel>
292 | 
293 | <panel>
294 | <paneltitle>Jose Cuervo 1800 Coleccion</paneltitle>
295 | <p>Jose Cuervo 1800 is reputed to be the second most expensive tequila...</p>
296 | </panel>
297 | </panelset>
298 | 
299 |
300 | 301 |

302 | Github issue 5
303 | Should the panelset element have selectedIndex?

304 | 305 |

306 | Github issue 25
307 | If HTML authors are encouraged to follow this separation, it inevitably begs the question about how CSS authors can correctly manage which type of display the element takes on in which conditions. This is currently "up in the air". One approach, prollyfilled here, is to use an attribute.

308 | 309 |

310 | Github issue 19
311 | Different visual metaphors have different selection (expansion) patterns. How can this be managed if the panelset element is predicated on a single selection model?

312 | 313 |

314 | Github issue 28
315 | When an AT is enabled on a touch device, only basic events are fired (mouse events, and in some circumstances blur/focus events). Is this something to consider?

316 |
317 | 318 |
319 |

The removable attribute

320 | 321 |

The removable attribute is a Boolean attribute that indicates that a panel element can be removed from the user interface. When a panel element is dismissed from the user interface, it is removed from the DOM.

322 | 323 |

When the removable attribute is present, a mechanism to dismiss the panel is included in the panel element header.

324 | 325 |
326 |
panel element with the removable attribute.
327 | panel element with removable attribute 328 |

329 | <panel removable>
330 | <paneltitle>Reminder</paneltitle>
331 | <p>
332 | Today is the first day of the rest of your life.
333 | </p>
334 | </panel>
335 | 
336 |
337 | 338 |
339 | 340 |
341 |

The expandable attribute

342 | 343 |

The expandable attribute indicates the expanded or collapsed state of a panel element.

344 | 345 |

When the expandable attribute is present, the paneltitle element becomes a control that allows the panel element to be toggled between an expanded or collapsed state.

346 | 347 |

When the expandable attribute is present and has no value, the panel is in a collapsed state. If the expandable attribute is not present, or is set to true, the panel is in an expanded state.

348 | 349 |
350 |
panel element with expandable attribute (collapsed state).
351 | Panel element with expandable attribute (collapsed state) 352 |

353 | 
354 | <panel expandable>
355 | <paneltitle>Today's news</paneltitle>
356 | <p>Something <a href="amazing.html">amazing</a> happened on the internet today...</p>
357 | </panel>
358 | 
359 |
360 | 361 |

362 | Github issue 20
363 | When you follow a link to a panel does it automatically expand?

364 |
365 | 366 |
367 |

The prefereddisplay attribute

368 | 369 |

The prefereddisplay attribute determines the initial visual representation of a panelset element containing one or more panel elements.

370 |
371 | 372 |
373 |

DOM interfaces

374 | 375 |
376 |

The panel element DOM interface

377 |
378 |
attribute HTMLElement headerElement
379 |
380 | Provides access to the header element for this panel 381 |
382 |
attribute HTMLElement contentElement
383 |
384 | Provides access to the content element for this panel 385 |
386 |
attribute HTMLElement tabProxyElement
387 |
388 | Provides access to the tab proxy element associated with this panel if it exists (if the panel is part of a panel set) or null 389 |
390 |
attribute DOMString expansionState
391 |
392 | Sets the panel's expansion state to "opened" or "closed" and manages associated accessibility attributes on relevant elements. // TODO: Should this be const instead of DOMString? 393 |
394 |
void toggleExpansionState()
395 |
396 | Inverts the current expansion state of the panel. 397 |
398 |
399 |
400 | 401 |
402 |

The panelset element DOM interface

403 |
404 |
attribute HTMLPanelElement activePanelElement
405 |
406 | Get or set an element as active (currently selected) panel element in this panel set 407 |
408 |
void selectNextTab()
409 |
410 | Selects/activates the next panel in this panel set in document order if any remain 411 |
412 |
void selectPreviousTab()
413 |
414 | Selects/activates the previous panel in this panel set in document order if any previous exist 415 |
416 |
417 |
418 |
419 | 420 |
421 |

User interfaces

422 |

This section is non-normative. [section is full of conformance requirements ??]

423 | 424 |

When the panel and paneltitle elements are used, the user agent should expose a user interface to the user. This user interface should include a visible title, and may optionally include an author provided icon or indicator of expandable state.

425 | 426 |

If the removable attribute is present, the user interface should include a control for dismissing the panel element from the user interface. The control should be focusable and have an accessible name.

427 | 428 |

If the expandable attribute is present, the paneltitle should become a control for toggling the panel between an expanded or collapsed state. The control must be focusable.

429 | 430 |
431 |

Composite tree

432 | 433 |
434 |
Elements and pseudo-elements of the composite tree of a panel element with a paneltitle element.

435 | Elements and pseudo-elements of the composite tree of a panel element with a paneltitle element 436 |
437 | 438 |
439 |
::--common-panel-header-box
440 |
Used for styling, it contains all the visual features of a panel header.
441 |
::--common-panel-header
442 |
When a panel element is used on its own, this has default styles and hooks. When a panel element is used as a child of a panelset element, this has special affordances.
443 |
::--common-panel-icon
444 |
Optional styling hook for an icon. Empty/blank/dimensionless by default, it can be used to display an icon via the CSS background:; property.
445 | Authors can also use the ::before pseudo-element to display a left-orientated indicator of the expandable state of the panel element.
446 |
::--common-panel-title
447 |
Author specified flow content which serves as the panel element's title.
448 |
::--common-panel-hook
449 |
Optional hook for a right-orientated indicator of the expandable state of the panel element.
450 |
::--common-panel-destroy
451 |
When the removable attribute is set, this displays a control for dismissing the panel element from the user interface. The control must be focusable and have an accessible name, otherwise it is hidden.
452 |
::--common-panel-content
453 |
Author provided content for the panel element.
454 |
455 |
456 | 457 |

458 | Github issue 27
459 | Should we include recommended/suggested interaction models here?

460 |
461 | 462 |
463 |

ARIA to accessibility API mapping guide

464 |

This section is non-normative.

465 | 466 |
467 |

ARIA roles

468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 |
ARIA roles mapping guide
ElementARIA role
panelgroup
paneltitle??
panelset??
484 |
485 | 486 |
487 |

ARIA states and properties

488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 |
ARIA states and properties mapping guide
AttributeARIA state/propertey
removable??
expandable??
501 |
502 |
503 | 504 | 505 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "common-panel", 3 | "version": "0.1.0", 4 | "devDependencies": { 5 | "grunt": "^0.4.5", 6 | "grunt-contrib-concat": "^0.5.1", 7 | "grunt-contrib-connect": "^0.11.2", 8 | "grunt-contrib-jshint": "^0.11.2", 9 | "grunt-contrib-nodeunit": "~0.4.1", 10 | "grunt-contrib-qunit": "^0.7.0", 11 | "grunt-contrib-uglify": "^0.9.1" 12 | }, 13 | "description": "Common panel and panelsets as custom element and directive prollyfills", 14 | "main": "Gruntfile.js", 15 | "dependencies": { 16 | "grunt-cli": "^0.1.13" 17 | }, 18 | "scripts": { 19 | "test": "echo \"Error: no test specified\" && exit 1" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/bkardell/common-panel.git" 24 | }, 25 | "author": "bkardell", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/bkardell/common-panel/issues" 29 | }, 30 | "homepage": "https://github.com/bkardell/common-panel" 31 | } 32 | -------------------------------------------------------------------------------- /panelset-cpreuse.less: -------------------------------------------------------------------------------- 1 | /* 2 | Pre-processors provide author-level reuse with is better, 3 | but ultimately yields the above CSS with it's problems 4 | */ 5 | 6 | @charset "utf-8"; 7 | // Global variables 8 | @import "../../../variables"; 9 | // Global mixins 10 | @import "../../../mixins/_mixins"; 11 | // Components 12 | // Font awesome 13 | //@import "../../../components/fonts/font-awesome"; 14 | @font-face { 15 | font-family: 'FontAwesome'; 16 | src: url('../fonts/fontawesome-webfont.eot?v=4.1.0'); 17 | src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); 18 | font-weight: normal; 19 | font-style: normal; 20 | } 21 | // Panelset - ALL modifiers need to be imported 22 | @import "../../../components/panelset/base"; 23 | @import "../../../components/panelset/modifiers/tabs"; 24 | @import "../../../components/panelset/modifiers/accordions"; 25 | @import "../../../components/panelset/modifiers/carousel"; 26 | 27 | // DEMOs 28 | /*Default is accordion*/ 29 | common-panel-set { 30 | .common-panel-tabs {display: none;} 31 | #aes.panelset.panel-accordion(); 32 | } 33 | .common-panel-tabs {display: none;} 34 | /*Default for #fooe, #bare, #bate*/ 35 | common-panel-set#fooe, common-panel-set#bare, common-panel-set#bate { 36 | #aes.panelset.panel-tabs(); 37 | .common-panel-tabs {display: block;} 38 | // Hide panel top border 39 | > common-panel { 40 | border-top: 0; 41 | // Hide title from accordion layout 42 | > .common-panel-header {display: none;} 43 | // Hide unselected panels 44 | &:not([expansion-state="opened"]) {display: none;} 45 | } 46 | } 47 | /*small screen for #fooe is displaying as carousel*/ 48 | @media screen and (max-width: 767px) { 49 | common-panel-set#fooe { 50 | #aes.panelset.panel-carousel(); 51 | > common-panel { 52 | #aes.border.top(@module-header-bottom-border-width, @module-header-bottom-border-style, @module-header-bottom-border-color); 53 | > .common-panel-header { 54 | display: block; 55 | float: none; 56 | } 57 | &:not([expansion-state="opened"]) { 58 | display: none; 59 | } 60 | } 61 | > .common-panel-tabs { 62 | > * {display: inline;} 63 | > .common-panel-header > span {display: none;} 64 | .common-panel-remove { display: none;} 65 | } 66 | } 67 | } 68 | /*small screen for #bare, #bate are displayed as accordion*/ 69 | @media screen and (max-width: 767px) { 70 | common-panel-set#bare, common-panel-set#bate { 71 | .common-panel-tabs {display: none;} 72 | #aes.panelset.panel-accordion(); 73 | #aes.border.top(@module-header-bottom-border-width, @module-header-bottom-border-style, @module-header-bottom-border-color); 74 | > common-panel { 75 | &:not([expansion-state="opened"]){ display: block;} 76 | &:not(:last-child) {border-bottom: 0;} 77 | > .common-panel-header { 78 | display: block; 79 | float: none; 80 | span { 81 | &:before {display: none;} 82 | } 83 | } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /panelset-without-pd.css: -------------------------------------------------------------------------------- 1 | /* 2 | This shows what it would be like just styling elements in instances of a vanilla 3 | common-panel-set according to media queries (no shadow dom). 4 | Note that any other stylesheet which talks about more instances of 'accordions' 5 | or 'tabs' for example will have to re-include the same styles and the only 6 | hint to the author about what any of these are trying to accomplish 7 | are in comments. Many many rules apply and override which causes context 8 | headaches too. 9 | */ 10 | @charset "utf-8"; 11 | @font-face { 12 | font-family: 'FontAwesome'; 13 | src: url('../fonts/fontawesome-webfont.eot?v=4.1.0'); 14 | src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); 15 | font-weight: normal; 16 | font-style: normal; 17 | } 18 | /*panelset base*/ 19 | common-panel-set { 20 | display: block; 21 | margin-bottom: 20px; 22 | } 23 | common-panel-set .common-panel-header { 24 | cursor: pointer; 25 | } 26 | /*panel base*/ 27 | common-panel { 28 | display: block; 29 | position: relative; 30 | font-size: 14px; 31 | font-family: Arial, Helvetica, sans-serif; 32 | margin: 0; 33 | padding: 0; 34 | color: #000000; 35 | background-color: #ffffff; 36 | -webkit-border-radius: 0; 37 | -moz-border-radius: 0; 38 | -ms-border-radius: 0; 39 | -o-border-radius: 0; 40 | border-radius: 0; 41 | border: 1px solid #cccccc; 42 | border: 1px solid rgba(0, 0, 0, 0.3); 43 | } 44 | common-panel:before, 45 | common-panel:after { 46 | content: " "; 47 | display: table; 48 | } 49 | common-panel:after { 50 | clear: both; 51 | } 52 | common-panel .common-panel-header { 53 | font-size: 16px; 54 | color: #000000; 55 | font-weight: 700; 56 | line-height: 20px; 57 | padding: 12px 20px 12px 20px; 58 | margin: 0; 59 | background-color: #f7f7f7; 60 | -webkit-border-radius: 0; 61 | -moz-border-radius: 0; 62 | -ms-border-radius: 0; 63 | -o-border-radius: 0; 64 | border-radius: 0; 65 | border-bottom: 1px solid #bbbbbb; 66 | } 67 | common-panel .common-panel-header .common-panel-remove > i { 68 | font-style: normal; 69 | font-weight: bold; 70 | font-size: 16px; 71 | } 72 | common-panel .common-panel-header .common-panel-remove > i:before { 73 | content: "x"; 74 | } 75 | common-panel > .common-panel-content { 76 | margin: 0; 77 | padding: 20px; 78 | } 79 | common-panel .common-panel-remove { 80 | font-family: Roboto, arial, sans-serif; 81 | border: 0; 82 | color: #000; 83 | background-color: transparent; 84 | position: absolute; 85 | top: 10px; 86 | right: 4px; 87 | -webkit-text-shadow: 1px 1px 1px #ffffff; 88 | -moz-text-shadow: 1px 1px 1px #ffffff; 89 | -ms-text-shadow: 1px 1px 1px #ffffff; 90 | -o-text-shadow: 1px 1px 1px #ffffff; 91 | text-shadow: 1px 1px 1px #ffffff; 92 | opacity: 0.5; 93 | filter: alpha(opacity=50); 94 | } 95 | common-panel .common-panel-remove i { 96 | font-style: normal; 97 | font-weight: bold; 98 | } 99 | common-panel .common-panel-remove i:before { 100 | content: "x"; 101 | } 102 | common-panel .common-panel-remove:hover { 103 | opacity: 0.9; 104 | filter: alpha(opacity=90); 105 | } 106 | common-panel[expansion-state]:not([expansion-state="opened"]) > *:not(.common-panel-header) { 107 | display: none; 108 | } 109 | common-panel[expansion-state] > div > span:after { 110 | float: right; 111 | content: "\f078"; 112 | font-family: FontAwesome; 113 | } 114 | common-panel[expansion-state="opened"] > div > span:after { 115 | float: right; 116 | content: "\f077"; 117 | font-family: FontAwesome; 118 | } 119 | /*Hide panel-header bottom border when closed*/ 120 | common-panel[expansion-state="closed"] > .common-panel-header { 121 | border-bottom: 0; 122 | } 123 | .common-panel-tabs { 124 | *zoom: 1; 125 | border-bottom: 1px solid #dddddd; 126 | font-family: Arial, Helvetica, sans-serif; 127 | } 128 | .common-panel-tabs:before, 129 | .common-panel-tabs:after { 130 | display: table; 131 | line-height: 0; 132 | content: ""; 133 | clear: both; 134 | } 135 | .common-panel-tabs .common-panel-header { 136 | float: left; 137 | margin-bottom: -1px; 138 | border: 1px solid transparent; 139 | line-height: 20px; 140 | margin-right: 2px; 141 | padding: 8px 12px; 142 | -webkit-border-radius: 0; 143 | -moz-border-radius: 0; 144 | -ms-border-radius: 0; 145 | -o-border-radius: 0; 146 | border-radius: 0; 147 | } 148 | .common-panel-tabs .common-panel-header:hover, 149 | .common-panel-tabs .common-panel-header:focus { 150 | background-color: #eeeeee; 151 | border-color: #dddddd #dddddd #dddddd; 152 | } 153 | .common-panel-tabs .common-panel-header[expansion-state="opened"], 154 | .common-panel-tabs .common-panel-header[expansion-state="opened"]:hover, 155 | .common-panel-tabs .common-panel-header[expansion-state="opened"]:focus { 156 | background-color: #ffffff; 157 | border-color: #dddddd #dddddd transparent; 158 | border-style: solid; 159 | border-width: 1px; 160 | color: #00324b; 161 | } 162 | .common-panel-tabs .common-panel-header span { 163 | font-size: 14px; 164 | } 165 | .common-panel-tabs .common-panel-header .common-panel-remove { 166 | font-family: Roboto, arial, sans-serif; 167 | border: 0; 168 | color: #000; 169 | background-color: transparent; 170 | position: absolute; 171 | top: 10px; 172 | right: 4px; 173 | -webkit-text-shadow: 1px 1px 1px #ffffff; 174 | -moz-text-shadow: 1px 1px 1px #ffffff; 175 | -ms-text-shadow: 1px 1px 1px #ffffff; 176 | -o-text-shadow: 1px 1px 1px #ffffff; 177 | text-shadow: 1px 1px 1px #ffffff; 178 | opacity: 0.5; 179 | filter: alpha(opacity=50); 180 | font-size: 16px; 181 | position: relative; 182 | top: 0; 183 | right: -8px; 184 | } 185 | .common-panel-tabs .common-panel-header .common-panel-remove i { 186 | font-style: normal; 187 | font-weight: bold; 188 | } 189 | .common-panel-tabs .common-panel-header .common-panel-remove i:before { 190 | content: "x"; 191 | } 192 | .common-panel-tabs .common-panel-header .common-panel-remove:hover { 193 | opacity: 0.9; 194 | filter: alpha(opacity=90); 195 | } 196 | .common-panel-tabs .common-panel-header .common-panel-remove > i { 197 | font-style: normal; 198 | font-weight: bold; 199 | } 200 | .common-panel-tabs .common-panel-header .common-panel-remove > i:before { 201 | content: "x"; 202 | } 203 | /*Default is accordion*/ 204 | common-panel-set .common-panel-tabs { 205 | display: none; 206 | } 207 | common-panel-set .common-panel-header span { 208 | overflow: hidden; 209 | display: block; 210 | -webkit-font-smoothing: antialiased; 211 | -moz-osx-font-smoothing: grayscale; 212 | } 213 | common-panel-set .common-panel-header span:before { 214 | display: inline-block; 215 | font-style: normal; 216 | font-weight: normal; 217 | content: "\f054"; 218 | font-family: FontAwesome; 219 | line-height: 1; 220 | font-size: 14px; 221 | width: 24px; 222 | text-align: center; 223 | } 224 | .common-panel-tabs { 225 | display: none; 226 | } 227 | /*Default for #fooe, #bare, #bate*/ 228 | common-panel-set#fooe, 229 | common-panel-set#bare, 230 | common-panel-set#bate { 231 | *zoom: 1; 232 | border-bottom: 1px solid #dddddd; 233 | font-family: Arial, Helvetica, sans-serif; 234 | } 235 | common-panel-set#fooe:before, 236 | common-panel-set#bare:before, 237 | common-panel-set#bate:before, 238 | common-panel-set#fooe:after, 239 | common-panel-set#bare:after, 240 | common-panel-set#bate:after { 241 | display: table; 242 | line-height: 0; 243 | content: ""; 244 | clear: both; 245 | } 246 | common-panel-set#fooe .common-panel-header, 247 | common-panel-set#bare .common-panel-header, 248 | common-panel-set#bate .common-panel-header { 249 | float: left; 250 | margin-bottom: -1px; 251 | border: 1px solid transparent; 252 | line-height: 20px; 253 | margin-right: 2px; 254 | padding: 8px 12px; 255 | -webkit-border-radius: 0; 256 | -moz-border-radius: 0; 257 | -ms-border-radius: 0; 258 | -o-border-radius: 0; 259 | border-radius: 0; 260 | } 261 | common-panel-set#fooe .common-panel-header:hover, 262 | common-panel-set#bare .common-panel-header:hover, 263 | common-panel-set#bate .common-panel-header:hover, 264 | common-panel-set#fooe .common-panel-header:focus, 265 | common-panel-set#bare .common-panel-header:focus, 266 | common-panel-set#bate .common-panel-header:focus { 267 | background-color: #eeeeee; 268 | border-color: #dddddd #dddddd #dddddd; 269 | } 270 | common-panel-set#fooe .common-panel-header[expansion-state="opened"], 271 | common-panel-set#bare .common-panel-header[expansion-state="opened"], 272 | common-panel-set#bate .common-panel-header[expansion-state="opened"], 273 | common-panel-set#fooe .common-panel-header[expansion-state="opened"]:hover, 274 | common-panel-set#bare .common-panel-header[expansion-state="opened"]:hover, 275 | common-panel-set#bate .common-panel-header[expansion-state="opened"]:hover, 276 | common-panel-set#fooe .common-panel-header[expansion-state="opened"]:focus, 277 | common-panel-set#bare .common-panel-header[expansion-state="opened"]:focus, 278 | common-panel-set#bate .common-panel-header[expansion-state="opened"]:focus { 279 | background-color: #ffffff; 280 | border-color: #dddddd #dddddd transparent; 281 | border-style: solid; 282 | border-width: 1px; 283 | color: #00324b; 284 | } 285 | common-panel-set#fooe .common-panel-header span, 286 | common-panel-set#bare .common-panel-header span, 287 | common-panel-set#bate .common-panel-header span { 288 | font-size: 14px; 289 | } 290 | common-panel-set#fooe .common-panel-header .common-panel-remove, 291 | common-panel-set#bare .common-panel-header .common-panel-remove, 292 | common-panel-set#bate .common-panel-header .common-panel-remove { 293 | font-family: Roboto, arial, sans-serif; 294 | border: 0; 295 | color: #000; 296 | background-color: transparent; 297 | position: absolute; 298 | top: 10px; 299 | right: 4px; 300 | -webkit-text-shadow: 1px 1px 1px #ffffff; 301 | -moz-text-shadow: 1px 1px 1px #ffffff; 302 | -ms-text-shadow: 1px 1px 1px #ffffff; 303 | -o-text-shadow: 1px 1px 1px #ffffff; 304 | text-shadow: 1px 1px 1px #ffffff; 305 | opacity: 0.5; 306 | filter: alpha(opacity=50); 307 | font-size: 16px; 308 | position: relative; 309 | top: 0; 310 | right: -8px; 311 | } 312 | common-panel-set#fooe .common-panel-header .common-panel-remove i, 313 | common-panel-set#bare .common-panel-header .common-panel-remove i, 314 | common-panel-set#bate .common-panel-header .common-panel-remove i { 315 | font-style: normal; 316 | font-weight: bold; 317 | } 318 | common-panel-set#fooe .common-panel-header .common-panel-remove i:before, 319 | common-panel-set#bare .common-panel-header .common-panel-remove i:before, 320 | common-panel-set#bate .common-panel-header .common-panel-remove i:before { 321 | content: "x"; 322 | } 323 | common-panel-set#fooe .common-panel-header .common-panel-remove:hover, 324 | common-panel-set#bare .common-panel-header .common-panel-remove:hover, 325 | common-panel-set#bate .common-panel-header .common-panel-remove:hover { 326 | opacity: 0.9; 327 | filter: alpha(opacity=90); 328 | } 329 | common-panel-set#fooe .common-panel-header .common-panel-remove > i, 330 | common-panel-set#bare .common-panel-header .common-panel-remove > i, 331 | common-panel-set#bate .common-panel-header .common-panel-remove > i { 332 | font-style: normal; 333 | font-weight: bold; 334 | } 335 | common-panel-set#fooe .common-panel-header .common-panel-remove > i:before, 336 | common-panel-set#bare .common-panel-header .common-panel-remove > i:before, 337 | common-panel-set#bate .common-panel-header .common-panel-remove > i:before { 338 | content: "x"; 339 | } 340 | common-panel-set#fooe .common-panel-tabs, 341 | common-panel-set#bare .common-panel-tabs, 342 | common-panel-set#bate .common-panel-tabs { 343 | display: block; 344 | } 345 | common-panel-set#fooe > common-panel, 346 | common-panel-set#bare > common-panel, 347 | common-panel-set#bate > common-panel { 348 | border-top: 0; 349 | } 350 | common-panel-set#fooe > common-panel > .common-panel-header, 351 | common-panel-set#bare > common-panel > .common-panel-header, 352 | common-panel-set#bate > common-panel > .common-panel-header { 353 | display: none; 354 | } 355 | common-panel-set#fooe > common-panel:not([expansion-state="opened"]), 356 | common-panel-set#bare > common-panel:not([expansion-state="opened"]), 357 | common-panel-set#bate > common-panel:not([expansion-state="opened"]) { 358 | display: none; 359 | } 360 | /*small screen for #fooe is displaying as carousel*/ 361 | @media screen and (max-width: 767px) { 362 | common-panel-set#fooe { 363 | position: relative; 364 | } 365 | common-panel-set#fooe > common-panel { 366 | min-height: 200px; 367 | } 368 | common-panel-set#fooe > common-panel .common-panel-header { 369 | background-color: transparent; 370 | border-bottom: 0; 371 | } 372 | common-panel-set#fooe > common-panel .common-panel-header span:after { 373 | display: none; 374 | } 375 | common-panel-set#fooe > .common-panel-tabs { 376 | display: block; 377 | position: absolute; 378 | z-index: 1; 379 | bottom: 5px; 380 | left: 40%; 381 | right: 40%; 382 | border: 0; 383 | } 384 | common-panel-set#fooe > .common-panel-tabs > .common-panel-header { 385 | background-color: #ccc; 386 | border: 2px solid #aaa; 387 | height: 6px; 388 | -webkit-border-radius: 14px; 389 | -moz-border-radius: 14px; 390 | -ms-border-radius: 14px; 391 | -o-border-radius: 14px; 392 | border-radius: 14px; 393 | } 394 | common-panel-set#fooe > .common-panel-tabs > .common-panel-header:hover, 395 | common-panel-set#fooe > .common-panel-tabs > .common-panel-header:focus { 396 | background-color: inherit; 397 | border-color: inherit; 398 | } 399 | common-panel-set#fooe > .common-panel-tabs > .common-panel-header[expansion-state="opened"], 400 | common-panel-set#fooe > .common-panel-tabs > .common-panel-header[expansion-state="opened"]:hover, 401 | common-panel-set#fooe > .common-panel-tabs > .common-panel-header[expansion-state="opened"]:focus { 402 | background-color: #fff; 403 | border: 2px solid #000; 404 | } 405 | common-panel-set#fooe > common-panel { 406 | border-top: 1px solid #bbbbbb; 407 | } 408 | common-panel-set#fooe > common-panel > .common-panel-header { 409 | display: block; 410 | float: none; 411 | } 412 | common-panel-set#fooe > common-panel:not([expansion-state="opened"]) { 413 | display: none; 414 | } 415 | common-panel-set#fooe > .common-panel-tabs > * { 416 | display: inline; 417 | } 418 | common-panel-set#fooe > .common-panel-tabs > .common-panel-header > span { 419 | display: none; 420 | } 421 | common-panel-set#fooe > .common-panel-tabs .common-panel-remove { 422 | display: none; 423 | } 424 | } 425 | /*small screen for #bare, #bate are displayed as accordion*/ 426 | @media screen and (max-width: 767px) { 427 | common-panel-set#bare, 428 | common-panel-set#bate { 429 | border-top: 1px solid #bbbbbb; 430 | } 431 | common-panel-set#bare .common-panel-tabs, 432 | common-panel-set#bate .common-panel-tabs { 433 | display: none; 434 | } 435 | common-panel-set#bare .common-panel-header span, 436 | common-panel-set#bate .common-panel-header span { 437 | overflow: hidden; 438 | display: block; 439 | -webkit-font-smoothing: antialiased; 440 | -moz-osx-font-smoothing: grayscale; 441 | } 442 | common-panel-set#bare .common-panel-header span:before, 443 | common-panel-set#bate .common-panel-header span:before { 444 | display: inline-block; 445 | font-style: normal; 446 | font-weight: normal; 447 | content: "\f054"; 448 | font-family: FontAwesome; 449 | line-height: 1; 450 | font-size: 14px; 451 | width: 24px; 452 | text-align: center; 453 | } 454 | common-panel-set#bare > common-panel:not([expansion-state="opened"]), 455 | common-panel-set#bate > common-panel:not([expansion-state="opened"]) { 456 | display: block; 457 | } 458 | common-panel-set#bare > common-panel:not(:last-child), 459 | common-panel-set#bate > common-panel:not(:last-child) { 460 | border-bottom: 0; 461 | } 462 | common-panel-set#bare > common-panel > .common-panel-header, 463 | common-panel-set#bate > common-panel > .common-panel-header { 464 | display: block; 465 | float: none; 466 | } 467 | common-panel-set#bare > common-panel > .common-panel-header span:before, 468 | common-panel-set#bate > common-panel > .common-panel-header span:before { 469 | display: none; 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /preferrential-display.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Preferrential Display Explainer 6 | 7 | 9 | 13 | 14 | 27 | 28 | 29 |
30 |

31 | This document decribes a proposal for a CSS mechanism and DOM APIs to 32 | aid in increasing the level of separation between visual and non-visual 33 | structures with runtime reuse. It is useful for Custom Elements, introduces potentially new 34 | explanations for how existing elements in the platform work. 35 |

36 |
37 | 38 |
39 |
40 | 41 |
42 |

Introduction

43 |

44 | When editing a document, an author attempts to mark it up semantically and control 45 | a separation of visual aspects through CSS. This allows various the user-agent 46 | and tools to use the same semantic data to provide any number of affordances 47 | for accessibility, navigation, styling and interaction. HTML elements are generally 48 | carefully constructed to define what they do that makes them unique, 49 | rather than very specifically how they look. This separation leaves room for 50 | certain experimentation and variance that is healthy for the long term adaptability 51 | of the platform. For example, because a 52 | <select> element doesn't strictly define all aspects of the 53 | visual properties, it was possible for mobile phones to provide a better user 54 | experience where visual real estate is very limited and click areas are shrunk. 55 |

56 |

57 | Similarly, an author may use various types on the <input> element, 58 | which, if undefined still defaults to type="text" and allows UAs like mobile 59 | devices provide native controls. In this aspect, the type attribute in 60 | some ways acts more like a hint than a requirement. This is, of course, not entirely 61 | true (input type is kind of a debacle actually, APIs actually vary making it improbable to 62 | use the intended 'text only' fallback without much pain) but it is a laudbile 63 | aim and provides a conceptual background for some of the rationale for this proposal. 64 |

65 | 66 |

These examples which already exist in the platform are good, but they are also potentially 67 | limited and somewhat magical. They can have impacts on more than visualization as well, to the 68 | end effect that they make it difficult for authors to deal with or innovate within.

69 | 70 |

Preferential Display is a more generalized and more capable extension of the type idea, 71 | allowing an author to specify preference hints of the type "when the media is screen and max-width is 767px, I would 72 | like to display elements #foo, #bar and #bat as tabsets if possible". At some low level it simply allows the user to manage 73 | a special attribute based on a media query from outside the markup and it desugars into existing platform code 74 | in JavaScript something like this: 75 |

/* This is basically a possible approach today */
 76 | matchMedia("screen and (max-width: 767px)").addListener(function(evt) { 
77 |     var element = Array.prototype.slice.call(document.querySelectorAll("#foo, #bar, #bat"));
78 |     elements.forEach(function (el) {
79 |         if (evt.matches) {
80 |             el.setAttribute("preferrential-display", "tabset");
81 |         } else {
82 |             el.removeAttribute("preferrential-display");
83 |         }
84 |     });
85 | })
86 | 87 |

88 | 89 |

90 | However, this would be a bit more than you'd like to manage in terms of boilerplate, and order should probably follow selector 91 | specificity if they have a generic means to add these, and so we can introduce a new API to manage it - something like this: 92 |

93 | 94 |
CSS.associateDisplayPrefernce({ 
95 |     mediaQuery: "screen and (max-width: 767px)",
96 |     selector: "#foo, #bar, #bat",
97 |     preference: "tabset"
98 | });
99 | 100 |

101 | This is considerably more conveneint, but ideally you'd be able to express this in CSS itself in a way that is familliar ala something like: 102 |

103 | 104 |
@media screen and (max-width: 767px) { 
105 |     #foo, #bar, #bat { preferred-display: tabset; }
106 | };
107 | 108 |

For example, 109 | it might be possible to offer a Preferrential Display for <select-timezone> elements 110 | which provides a map-like interface similar to those used for choosing timezones in a desktop interface, 111 | but ultimately provides a common <select> view on the same DOM, or a <common-panel-set> 112 | which can be displayed as any number of visualizations (tabset, accordion, deck or carousel, for example) 113 | but has identical structures and accessibility concerns. It is useful then for an author to be able to 114 | state "under these media conditions I prefer a tabset view if you have it". 115 |

116 | 117 |

The design of this proposal is intended to be such that it introduces minimal new concepts, but offers 118 | new capabilities which answer some existing questions, highlight the importance of this separation, 119 | and provide new opportunities for experiementation. 120 |

121 | 122 |
123 |

Aims and Requirements

124 | 134 | To these points, what I am proposing is a single, display oriented attribute which is maintained via CSS which allows CSS to express informtation that it can select upon in normal fashion and allow experimentation with the various pieces of the platform that are already in place. 135 |
136 |
137 |

Why not...

138 | 149 |
150 | 151 |

Vanilla CSS example without Preferred Display

152 |

153 |       
154 | 
155 |       

Preprocessor authoring example without Preferred Display

156 |

157 | 
158 |     
159 | 160 |
161 |

Preferred Display

162 |

163 | 164 | An element's Preferential Display is determined by whether or not a Media Query has activated it. 165 | If the preferential display has not been activated by a Media Query, setting it has no effect. Thus: 166 |
167 |

/* this has no effect as the rule's preferrential-display is not activated by a MediaQuery */ 
168 | .foo {
169 |     preferred-display: map;
170 | }
171 |
172 | /* this rule defines a preferrential-display which can be activated by a MediaQuery */
173 | @media screen and (min-width: 767px) {
174 |     #foo {
175 |         preferred-display: tabset;
176 |     }
177 | }
178 |
179 |
180 | Upon activation, the media query relays the preferred display value to the preferred-display attribute in the DOM. This attribute is 181 | settable in markup, like type, but once set is read-only except via the CSS provided interface. This design 182 | allows that only a media query can cause CSS to change the DOM and simply prevents cycles which would normally prevent this sort of thing. Its presence in the DOM allows it to be 183 | easily used in normal CSS and JavaScript to do simple adaptations. MediaQueryList and listners can be used to explain how this works 184 | fairly well today. 185 |

186 | 187 |
188 | 189 | -------------------------------------------------------------------------------- /prototype/angular-panels.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | "use strict"; 3 | var lastUid = 0, 4 | nextUid = function() { 5 | return "-cp-" + (++lastUid); 6 | }, 7 | ensureId = function(el) { 8 | el.id = el.id || nextUid(); 9 | }, 10 | find = function(scope, query) { 11 | ensureId(scope); 12 | return scope.querySelector("#" + scope.id + query); 13 | }, 14 | findAll = function(scope, query) { 15 | ensureId(scope); 16 | return Array.prototype.slice.call(scope.querySelectorAll("#" + scope.id + query)); 17 | }, 18 | manageBooleanAttr = function(el, attr, condition) { 19 | if (condition) { 20 | el.setAttribute(attr, "true"); 21 | } else if (el.hasAttribute(attr)) { 22 | el.removeAttribute(attr); 23 | } 24 | }, 25 | FOCUS_DELAY = 50, 26 | templates = { 27 | tabset: "
{{tabs}}", 28 | tab: "
", 29 | tabcontent: "
" 30 | }, 31 | hideRemovable = function (el) { 32 | var btn = el.querySelector(".common-panel-remove"); 33 | if (btn) { 34 | btn.style.display = "none"; 35 | } 36 | }, 37 | attachDescriptorBefore = function (element, description) { 38 | var descriptor = document.createElement("span"); 39 | descriptor.classList.add("visually-hidden"); 40 | descriptor.innerHTML = " " + description + " "; 41 | element.parentElement.insertBefore(descriptor, element); 42 | }, 43 | sheet = document.createElement("style"); 44 | 45 | sheet.innerHTML = "@charset \"utf-8\";common-panel-set { display: block; margin-bottom: 20px;}common-panel-set .common-panel-header { cursor: pointer;}common-panel { display: block; position: relative; font-size: 13px; font-family: Arial, Helvetica, sans-serif; margin: 0; padding: 0; color: #000000; background-color: #ffffff; -webkit-border-radius: 0; -moz-border-radius: 0; -ms-border-radius: 0; -o-border-radius: 0; border-radius: 0; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.3); -webkit-box-shadow: 0 2px 2px 0 #cccccc; -moz-box-shadow: 0 2px 2px 0 #cccccc; box-shadow: 0 2px 2px 0 #cccccc; -webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.175); -moz-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.175); box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.175);}common-panel:before,common-panel:after { content: \" \"; display: table;}common-panel:after { clear: both;}common-panel .common-panel-header { font-size: 16px; color: #000000; font-weight: 700; line-height: 20px; padding: 10px 15px 10px 15px; margin: 0; background-color: #f7f7f7; -webkit-border-radius: 0 0 0 0; -moz-border-radius: 0 0 0 0; -ms-border-radius: 0 0 0 0; -o-border-radius: 0 0 0 0; border-radius: 0 0 0 0; border-bottom: 1px solid #bbbbbb;}common-panel > .common-panel-content { margin: 0; padding: 0;}common-panel .common-panel-remove { -webkit-text-shadow: 1px 1px 1px #ffffff; -moz-text-shadow: 1px 1px 1px #ffffff; -ms-text-shadow: 1px 1px 1px #ffffff; -o-text-shadow: 1px 1px 1px #ffffff; text-shadow: 1px 1px 1px #ffffff; opacity: 0.5; filter: alpha(opacity=50); font-family: Roboto, arial, sans-serif; font-size: 20px; position: absolute; top: 10px; right: 4px; line-height: 20px; font-weight: bold; border: 0; color: #000000; background-color: transparent;}common-panel .common-panel-remove:hover { opacity: 0.9; filter: alpha(opacity=90);}common-panel[expansion-state]:not([expansion-state=\"opened\"]) > *:not(.common-panel-header-box) { display: none;}common-panel[expansion-state] > .common-panel-header-box > .common-panel-header > span::after { float: right; content: \"\\25B8\"; font-size: 20px;}common-panel[expansion-state=\"opened\"] > .common-panel-header-box > .common-panel-header > span::after { float: right; content: \"\\25BE\"; font-size: 20px;}common-panel.borderless { background-color: transparent; -webkit-border-radius: 0; -moz-border-radius: 0; -ms-border-radius: 0; -o-border-radius: 0; border-radius: 0; border: 0 solid #cccccc; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none;}common-panel.borderless .common-panel-header { padding: 10px 0 10px 0; margin: 0; background-color: transparent; -webkit-border-radius: 0; -moz-border-radius: 0; -ms-border-radius: 0; -o-border-radius: 0; border-radius: 0; border-bottom: 1px solid #bbbbbb;}common-panel[expansion-state=\"closed\"] > .common-panel-header-box > .common-panel-header { border-bottom: 0; -webkit-border-radius: 0; -moz-border-radius: 0; -ms-border-radius: 0; -o-border-radius: 0; border-radius: 0;}common-panel.minus-plus-panel[expansion-state] > .common-panel-header-box > .common-panel-header > span::after { content: \"\\002B\"; font-size: 20px;}common-panel.minus-plus-panel[expansion-state=\"opened\"] > .common-panel-header-box > .common-panel-header > span::after { content: \"\\2212\"; font-size: 20px;}common-panel[expansion-state=\"closed\"] .common-panel-header { background-color: #f0f0f0; margin: 0; color: #000000;}common-panel[expansion-state=\"opened\"] .common-panel-header { background-color: #f7f7f7; margin: 0; color: #000000;}.cp-visually-hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: none;}.common-panel-tabs { *zoom: 1; border-bottom: 1px solid #dddddd; font-size: 13px; font-family: Arial, Helvetica, sans-serif; color: #000000; background-color: transparent; -webkit-border-radius: 0 0 0 0; -moz-border-radius: 0 0 0 0; -ms-border-radius: 0 0 0 0; -o-border-radius: 0 0 0 0; border-radius: 0 0 0 0;}.common-panel-tabs::before,.common-panel-tabs::after { display: table; line-height: 0; content: \"\"; clear: both;}.common-panel-tabs .common-panel-header { float: left; margin-bottom: -1px; border: 1px solid transparent; line-height: 20px; font-weight: 400; margin-right: 2px; padding: 8px 12px; border-right-color: transparent; -webkit-border-radius: 0 0 0 0; -moz-border-radius: 0 0 0 0; -ms-border-radius: 0 0 0 0; -o-border-radius: 0 0 0 0; border-radius: 0 0 0 0;}.common-panel-tabs .common-panel-header:hover,.common-panel-tabs .common-panel-header:focus { background-color: #eeeeee; border-color: #dddddd #dddddd #dddddd #dddddd;}.common-panel-tabs .common-panel-header[aria-selected],.common-panel-tabs .common-panel-header[aria-selected]:hover,.common-panel-tabs .common-panel-header[aria-selected]:focus { background-color: #ffffff; border-color: #dddddd #dddddd transparent; border-style: solid; border-width: 1px; color: #00324b; -webkit-border-radius: 0 0 0 0; -moz-border-radius: 0 0 0 0; -ms-border-radius: 0 0 0 0; -o-border-radius: 0 0 0 0; border-radius: 0 0 0 0;}.common-panel-tabs .common-panel-header .common-panel-remove { -webkit-text-shadow: 1px 1px 1px #ffffff; -moz-text-shadow: 1px 1px 1px #ffffff; -ms-text-shadow: 1px 1px 1px #ffffff; -o-text-shadow: 1px 1px 1px #ffffff; text-shadow: 1px 1px 1px #ffffff; opacity: 0.5; filter: alpha(opacity=50); font-family: Roboto, arial, sans-serif; font-size: 20px; position: absolute; top: 10px; right: 4px; line-height: 20px; font-weight: bold; border: 0; color: #000000; background-color: transparent;}.common-panel-tabs .common-panel-header .common-panel-remove:hover { opacity: 0.9; filter: alpha(opacity=90);}common-panel-set[preferred-display=\"tabset\"] > common-panel { border-top: 0;}common-panel-set[preferred-display=\"tabset\"] > common-panel > .common-panel-header-box > .common-panel-header { display: none;}common-panel-set[preferred-display=\"tabset\"] > common-panel:not([expansion-state=\"opened\"]) { display: none;}common-panel-set[preferred-display=\"tabset\"] > common-panel { -webkit-border-radius: 0 0 0 0; -moz-border-radius: 0 0 0 0; -ms-border-radius: 0 0 0 0; -o-border-radius: 0 0 0 0; border-radius: 0 0 0 0;}common-panel-set:not([preferred-display]) .common-panel-header,common-panel-set[preferred-display=\"accordion\"] .common-panel-header { background-color: #f0f0f0; margin: 0;}common-panel-set:not([preferred-display]) .common-panel-header span,common-panel-set[preferred-display=\"accordion\"] .common-panel-header span { overflow: hidden; display: block; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}common-panel-set:not([preferred-display]) .common-panel-header span::before,common-panel-set[preferred-display=\"accordion\"] .common-panel-header span::before { display: inline-block; font-style: normal; font-weight: normal; line-height: 1; font-size: 14px; width: 12px; text-align: center;}common-panel-set:not([preferred-display]) .common-panel-header:hover,common-panel-set[preferred-display=\"accordion\"] .common-panel-header:hover { text-decoration: none;}common-panel-set:not([preferred-display]) .common-panel-tabs,common-panel-set[preferred-display=\"accordion\"] .common-panel-tabs { display: none;}common-panel-set:not([preferred-display]) > common-panel:not(:last-child),common-panel-set[preferred-display=\"accordion\"] > common-panel:not(:last-child) { margin-bottom: -1px;}common-panel-set:not([preferred-display]) > common-panel > .common-panel-header span::before,common-panel-set[preferred-display=\"accordion\"] > common-panel > .common-panel-header span::before { display: none;}common-panel-set:not([preferred-display]) > common-panel[expansion-state=\"opened\"] .common-panel-header,common-panel-set[preferred-display=\"accordion\"] > common-panel[expansion-state=\"opened\"] .common-panel-header { background-color: #f7f7f7; color: #000000;}common-panel-set[preferred-display=\"carousel\"] { position: relative;}common-panel-set[preferred-display=\"carousel\"] > common-panel { min-height: 200px;}common-panel-set[preferred-display=\"carousel\"] > common-panel > .common-panel-header-box > .common-panel-header span::after { display: none;}common-panel-set[preferred-display=\"carousel\"] > common-panel .common-panel-header { background-color: transparent; border-bottom: 0; color: #000000;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs { display: block; position: absolute; z-index: 1; bottom: 10px; left: 90%; right: 0; border: 0; background-color: transparent;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header { background-color: #ccc; border: 2px solid #aaa; height: 6px; margin-right: 2px; -webkit-border-radius: 14px; -moz-border-radius: 14px; -ms-border-radius: 14px; -o-border-radius: 14px; border-radius: 14px;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header:hover,common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header:focus { background-color: #ccc; border-color: inherit;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header[aria-selected],common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header[aria-selected]:hover,common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header[aria-selected]:focus { background-color: #fff; border: 2px solid #000;}common-panel-set[preferred-display=\"carousel\"] > common-panel:not([expansion-state=\"opened\"]) { display: none;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > * { display: inline;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header > span { display: none;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs .common-panel-remove { display: none;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header { padding: 1px 4px;}common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header[aria-selected],common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header[aria-selected]:hover,common-panel-set[preferred-display=\"carousel\"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header[aria-selected]:focus { -webkit-border-radius: 14px; -moz-border-radius: 14px; -ms-border-radius: 14px; -o-border-radius: 14px; border-radius: 14px;}.visually-hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: none;}"; 46 | document.head.appendChild(sheet); 47 | 48 | angular.module("commonPanels", []) 49 | .directive("transcludeReplace", ["$log", function($log) { 50 | return { 51 | terminal: true, 52 | restrict: "EA", 53 | link: function($scope, $element, $attr, ctrl, transclude) { 54 | if (!transclude) { 55 | $log.error("orphan", 56 | "No parent directive that requires a transclusion found."); 57 | return; 58 | } 59 | transclude(function(clone) { 60 | if (clone.length) { 61 | $element.replaceWith(clone); 62 | } else { 63 | $element.remove(); 64 | } 65 | }); 66 | } 67 | }; 68 | }]) 69 | .directive("commonPanelSet", ["$interpolate", function($interpolate) { 70 | var _registerChild = function (ownElement, panelElement, tabElement) { 71 | var headerElement = panelElement.headerElement, 72 | contentElement = panelElement.contentElement, 73 | tabsContainer = find(ownElement, ">.common-panel-tabs"); 74 | 75 | 76 | angular.element(tabsContainer).append(tabElement); 77 | 78 | if (!panelElement.hasAttribute("is-removable")) { 79 | hideRemovable(tabElement); 80 | } 81 | 82 | ownElement.setAttribute("role", "tablist"); 83 | 84 | /* TODO: each of these has a related panel which needs wiring */ 85 | panelElement.setAttribute("role", "presentation"); 86 | headerElement.setAttribute("role", "presentation"); 87 | contentElement.setAttribute("role", "tabpanel"); 88 | tabElement.firstElementChild.setAttribute("role", "tab"); 89 | 90 | // TODO: This seems bad, lets name the thing 91 | tabElement.firstElementChild.setAttribute("aria-controls", contentElement.id); 92 | 93 | // TODO: This seems bad, lets name the thing 94 | headerElement.firstElementChild.setAttribute("role", "tab"); 95 | 96 | if (panelElement.getAttribute("expansion-state") === "opened") { 97 | ownElement.selectedIndex = ownElement.children.length-1; 98 | } 99 | tabElement.addEventListener("click", function() { 100 | // console.log("caught click %o", panelElement); 101 | ownElement.activePanelElement = panelElement; 102 | }, false); 103 | headerElement.addEventListener("click", function() { 104 | // console.log("caught click %o", panelElement); 105 | ownElement.activePanelElement = panelElement; 106 | }, false); 107 | 108 | 109 | // before the upgrade is finished this wont exist 110 | if (ownElement.common_panels) { 111 | if (panelElement.getAttribute("expansion-state") === "opened" || ownElement.common_panels.length === 1) { 112 | ownElement.activePanelElement = panelElement; 113 | } else { 114 | panelElement.expansionState = "closed"; 115 | } 116 | } 117 | }; 118 | return { 119 | restrict: "E", 120 | controller: ["$scope", "$element", function($scope, $element) { 121 | this.addPanel = function(panelElement, panelScope) { 122 | var ownElement = $element[0]; 123 | var tab = angular.element($interpolate(templates.tab)(panelScope))[0]; 124 | find(tab, ">* .common-panel-title").innerHTML = panelScope.title; 125 | _registerChild(ownElement, panelElement, tab); 126 | }; 127 | }], 128 | link: function($scope, $element) { 129 | var ownElement = $element[0]; 130 | ownElement.selectedIndex = ownElement.selectedIndex || -1; 131 | 132 | Object.defineProperties(ownElement, { 133 | common_panels: { 134 | get: function() { 135 | return findAll(this, ">common-panel"); 136 | } 137 | }, 138 | activePanelElement: { 139 | get: function() { 140 | // TOOD: add a .common_panels definition 141 | // TODO: best way to find the expanded element? 142 | return ownElement.common_panels[ownElement.selectedIndex]; 143 | }, 144 | set: function(panel) { 145 | var panels = ownElement.common_panels; 146 | ownElement.selectedIndex = panels.indexOf(panel); 147 | ownElement.selectedIndex = (ownElement.selectedIndex !== -1) ? ownElement.selectedIndex : 0; 148 | // setting a panel element active has a side effect of making siblings inactive... 149 | panels.forEach(function(curPanel, i) { 150 | // console.log(curPanel); 151 | curPanel.expansionState = (i === ownElement.selectedIndex) ? "opened" : "closed"; 152 | }); 153 | } 154 | }, 155 | // TODO: maybe this shouldnt be an instance method, it is on original proto too 156 | setFocusForActivePanel: { 157 | value: function (force) { 158 | var headerElement, tabProxyElement, activePanelElement = this.activePanelElement; 159 | if (activePanelElement) { 160 | headerElement = activePanelElement.headerElement; 161 | tabProxyElement = activePanelElement.tabProxyElement; 162 | // TODO: do we still need last set/mutations? 163 | if (force || lastSet) { 164 | if (headerElement.firstElementChild.offsetHeight !== 0) { 165 | headerElement.firstElementChild.focus(); 166 | } else { 167 | tabProxyElement.focus(); 168 | } 169 | } 170 | } 171 | } 172 | }, 173 | selectNextTab: { 174 | value: function () { 175 | var self = this, panels = this.common_panels; 176 | if (this.selectedIndex < panels.length-1) { 177 | this.selectedIndex++; 178 | } 179 | this.activePanelElement = this.common_panels[this.selectedIndex]; 180 | setTimeout(function() { 181 | self.setFocusForActivePanel(true); 182 | }, FOCUS_DELAY); 183 | } 184 | }, 185 | selectPreviousTab: { 186 | value: function () { 187 | var self = this; 188 | if (this.selectedIndex > 0) { 189 | this.selectedIndex--; 190 | } 191 | this.activePanelElement = this.common_panels[this.selectedIndex]; 192 | setTimeout(function() { 193 | self.setFocusForActivePanel(true); 194 | }, FOCUS_DELAY); 195 | } 196 | } 197 | 198 | /* TODO: 199 | add selectNextTab / selectPreviousTab on ownElement + keydown listener for keyCode 37-40 200 | 201 | Add _registerChild? Not sure angular will like that or why you would use it 202 | figure that out, it is very similar to addPanel in the controller 203 | 204 | add _deregisterChild? Again, if there is cleanup to do, like the 205 | transferral of focus/selected index, etc 206 | */ 207 | }); 208 | ownElement.activePanelElement = findAll(ownElement, ">common-panel")[ownElement.selectedIndex]; 209 | ownElement.addEventListener("keydown", function(evt) { 210 | // console.log("caught keys"); 211 | if (document.activeElement.classList.contains("common-panel-header")){ 212 | 213 | switch (evt.keyCode) { 214 | 215 | case 37: 216 | // console.log("pressed left"); /* previous tab */ 217 | ownElement.selectPreviousTab(); 218 | break; 219 | case 38: 220 | // console.log("pressed up"); 221 | ownElement.selectPreviousTab(); 222 | evt.preventDefault(); 223 | break; 224 | case 39: 225 | // console.log("pressed right"); /* next tab */ 226 | ownElement.selectNextTab(); 227 | break; 228 | case 40: 229 | // console.log("pressed down"); 230 | ownElement.selectNextTab(); 231 | evt.preventDefault(); 232 | break; 233 | } 234 | } 235 | }); 236 | // TODO: add key handler here which calls selectNextTab or selectPreviousTab 237 | 238 | }, 239 | template: templates.tabset, 240 | transclude: true 241 | }; 242 | }]) 243 | .directive("commonPanel", ["$interpolate", "$sce", function($interpolate, $sce) { 244 | return { 245 | require: "^?commonPanelSet", 246 | restrict: "E", 247 | scope: { 248 | /*, 249 | title: "@", 250 | expansion: "@expansionState", TODO: find out how to deal with boolean attrs 251 | removable: "@isRemovable"*/ 252 | }, 253 | controller: function() { 254 | // calculations controls management here? 255 | }, 256 | transclude: true, 257 | template: templates.tab + templates.tabcontent, 258 | link: function(scope, element, attrs, commonPanelSetCtrl) { 259 | var ownElement = element[0], 260 | contentElement, 261 | headerElement, 262 | titleElement, 263 | isExpandable; 264 | 265 | Object.defineProperties(ownElement, { 266 | headerElement: { 267 | get: function() { 268 | return find(this, ">.common-panel-header-box"); 269 | } 270 | }, 271 | contentElement: { 272 | get: function() { 273 | return find(this, "> .common-panel-content"); 274 | } 275 | }, 276 | tabProxyElement: { 277 | get: function() { 278 | var ret; 279 | if (this.parentElement && this.parentElement.tagName === "COMMON-PANEL-SET") { 280 | ret = find(this.parentElement, ">.common-panel-tabs>.common-panel-header-box>.common-panel-header[aria-controls=\"" + this.contentElement.id + "\"]"); 281 | } 282 | return ret; 283 | } 284 | }, 285 | expansionState: { 286 | set: function(state) { 287 | var evt, 288 | contentElement = this.contentElement, 289 | isOpen = "opened" === state, 290 | tabProxyElement = this.tabProxyElement; 291 | 292 | this.setAttribute("expansion-state", state); 293 | manageBooleanAttr(contentElement, "aria-hidden", !isOpen); 294 | manageBooleanAttr(contentElement, "aria-expanded", isOpen); 295 | contentElement.setAttribute("tabindex", (isOpen) ? "0" : "-1"); 296 | 297 | // TODO: Is there a 'more angular' way to do this? do we need to worry? 298 | if (tabProxyElement) { 299 | manageBooleanAttr(tabProxyElement, "aria-selected", isOpen); 300 | tabProxyElement.setAttribute("tabindex", (isOpen) ? "0" : "-1"); 301 | } 302 | 303 | if (isOpen) { 304 | evt = document.createEvent("CustomEvent"); 305 | evt.initCustomEvent("_activate", true, true, null); 306 | this.dispatchEvent(evt); 307 | } 308 | } 309 | }, 310 | toggleExpansionState: { 311 | value: function() { 312 | var current = this.getAttribute("expansion-state"); 313 | var isClosedOrDefault = (current === null || current === "closed"); 314 | // console.log("called toggle on " + this + " " + isClosedOrDefault); 315 | this.expansionState = (isClosedOrDefault) ? "opened" : "closed"; 316 | } 317 | } 318 | /* TODO: add toggle expansion state and destructor which handles communication back up 319 | so panelset can manage focus/activation properly 320 | add 321 | */ 322 | }); 323 | ensureId(ownElement); 324 | ownElement.setAttribute("role", "group"); 325 | 326 | contentElement = ownElement.contentElement; 327 | ensureId(ownElement.contentElement); 328 | 329 | headerElement = ownElement.headerElement; 330 | ensureId(ownElement.headerElement); 331 | 332 | attachDescriptorBefore(headerElement.querySelector(".common-panel-remove"), "panel"); 333 | attachDescriptorBefore(contentElement.firstElementChild, "panel content"); 334 | 335 | isExpandable = ownElement.hasAttribute("expansion-state"); 336 | /* Tricky, this has to be kind of a 'non-element' else it is currently circular */ 337 | findAll(contentElement, ">common-panel-title").forEach(function(potential) { 338 | titleElement = potential.parentElement.removeChild(potential); 339 | }); 340 | 341 | scope.title = ""; 342 | try { 343 | scope.title = $sce.trustAsHtml($interpolate(titleElement.innerHTML)(scope.$parent)); 344 | } catch (e) { 345 | 346 | } 347 | // TODO: see if there is a way to map stupid boolean attributes 348 | scope.isRemovable = ownElement.hasAttribute("is-removable"); 349 | 350 | // TODO: this is only relevent if the thing is expandable or is inside a panelset 351 | headerElement.firstElementChild.setAttribute("aria-controls", contentElement.id); 352 | scope.identifiers = { 353 | panel: ownElement.id, 354 | content: contentElement.id, 355 | header: headerElement.id 356 | }; 357 | 358 | if (!scope.isRemovable) { 359 | hideRemovable(headerElement); 360 | } 361 | // wire up aria controls 362 | if (commonPanelSetCtrl) { 363 | // copy title into parent scope - TODO: +comment on why is the parent scope necessary here? iteration? 364 | scope.$parent.title = scope.title; 365 | commonPanelSetCtrl.addPanel(ownElement, scope.$parent); 366 | } else { 367 | //TODO: wire up expansion toggle? 368 | if (isExpandable || scope.isRemovable) 369 | headerElement.addEventListener("click", function(evt) { 370 | if (evt.target.matches(".common-panel-remove") || 371 | (evt.target.parentElement && evt.target.parentElement.matches(".common-panel-remove"))) { 372 | ownElement.parentElement.removeChild(ownElement); 373 | } else if (ownElement.hasAttribute("expansion-state")) { 374 | ownElement.toggleExpansionState(); 375 | ownElement.setAttribute( 376 | "expansion-state", (ownElement.getAttribute("expansion-state") === "opened") ? "opened" : "closed" 377 | ); 378 | } 379 | }, false); 380 | 381 | if (isExpandable) { 382 | headerElement.addEventListener("keydown", function(evt) { 383 | if (evt.keyCode === 32) { 384 | ownElement.toggleExpansionState(); 385 | } 386 | }, false); 387 | } 388 | // TODO: clean this up? the angular boolean attribute stuff is weird 389 | 390 | 391 | 392 | } 393 | 394 | if (window.MediaQueryMappings && window.MediaQueryMappings.recalc) { 395 | setTimeout(function () { 396 | window.MediaQueryMappings.recalc(); 397 | }, 10); 398 | } 399 | } 400 | }; 401 | }]); 402 | 403 | })(window.angular); 404 | -------------------------------------------------------------------------------- /prototype/custom-elements.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (C) 2014 by WebReflection 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | */ 23 | (function(window, document, Object, REGISTER_ELEMENT) { 24 | 'use strict'; 25 | 26 | // in case it's there or already patched 27 | if (REGISTER_ELEMENT in document) return; 28 | 29 | // DO NOT USE THIS FILE DIRECTLY, IT WON'T WORK 30 | // THIS IS A PROJECT BASED ON A BUILD SYSTEM 31 | // THIS FILE IS JUST WRAPPED UP RESULTING IN 32 | // build/document-register-element.js 33 | // and its .max.js counter part 34 | 35 | var 36 | // IE < 11 only + old WebKit for attributes + feature detection 37 | EXPANDO_UID = '__' + REGISTER_ELEMENT + (Math.random() * 10e4 >> 0), 38 | 39 | // shortcuts and costants 40 | EXTENDS = 'extends', 41 | DOM_ATTR_MODIFIED = 'DOMAttrModified', 42 | DOM_SUBTREE_MODIFIED = 'DOMSubtreeModified', 43 | PREFIX_TAG = '<', 44 | PREFIX_IS = '=', 45 | 46 | // valid and invalid node names 47 | validName = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/, 48 | invalidNames = [ 49 | 'ANNOTATION-XML', 50 | 'COLOR-PROFILE', 51 | 'FONT-FACE', 52 | 'FONT-FACE-SRC', 53 | 'FONT-FACE-URI', 54 | 'FONT-FACE-FORMAT', 55 | 'FONT-FACE-NAME', 56 | 'MISSING-GLYPH' 57 | ], 58 | 59 | // registered types and their prototypes 60 | types = [], 61 | protos = [], 62 | 63 | // to query subnodes 64 | query = '', 65 | 66 | // html shortcut used to feature detect 67 | documentElement = document.documentElement, 68 | 69 | // ES5 inline helpers || basic patches 70 | indexOf = types.indexOf || function(v) { 71 | /* jshint -W035 */ 72 | for (var i = this.length; i-- && this[i] !== v;) {} 73 | return i; 74 | }, 75 | 76 | // other helpers / shortcuts 77 | OP = Object.prototype, 78 | hOP = OP.hasOwnProperty, 79 | iPO = OP.isPrototypeOf, 80 | 81 | defineProperty = Object.defineProperty, 82 | gOPD = Object.getOwnPropertyDescriptor, 83 | gOPN = Object.getOwnPropertyNames, 84 | gPO = Object.getPrototypeOf, 85 | /* jshint -W035 */ 86 | sPO = Object.setPrototypeOf, 87 | 88 | /* jshint -W103 */ 89 | hasProto = !!Object.__proto__, 90 | 91 | // used to create unique instances 92 | /* jshint -W058 */ 93 | create = Object.create || function Bridge(proto) { 94 | // silly broken polyfill probably ever used but short enough to work 95 | return proto ? ((Bridge.prototype = proto), new Bridge) : this; 96 | }, 97 | 98 | // will set the prototype if possible 99 | // or copy over all properties 100 | setPrototype = sPO || ( 101 | hasProto ? 102 | function(o, p) { 103 | /* jshint -W103 */ 104 | o.__proto__ = p; 105 | return o; 106 | } : ( 107 | (gOPN && gOPD) ? 108 | (function() { 109 | function setProperties(o, p) { 110 | for (var 111 | key, 112 | names = gOPN(p), 113 | i = 0, length = names.length; i < length; i++) { 114 | key = names[i]; 115 | if (!hOP.call(o, key)) { 116 | defineProperty(o, key, gOPD(p, key)); 117 | } 118 | } 119 | } 120 | return function(o, p) { 121 | do { 122 | setProperties(o, p); 123 | /* jshint -W084 */ 124 | } while (p = gPO(p)); 125 | return o; 126 | }; 127 | }()) : 128 | function(o, p) { 129 | for (var key in p) { 130 | o[key] = p[key]; 131 | } 132 | return o; 133 | } 134 | )), 135 | 136 | // DOM shortcuts and helpers, if any 137 | 138 | MutationObserver = window.MutationObserver || 139 | window.WebKitMutationObserver, 140 | 141 | HTMLElementPrototype = ( 142 | window.HTMLElement || 143 | window.Element || 144 | window.Node 145 | ).prototype, 146 | 147 | cloneNode = HTMLElementPrototype.cloneNode, 148 | setAttribute = HTMLElementPrototype.setAttribute, 149 | 150 | // replaced later on 151 | createElement = document.createElement, 152 | 153 | // shared observer for all attributes 154 | attributesObserver = MutationObserver && { 155 | attributes: true, 156 | characterData: true, 157 | attributeOldValue: true 158 | }, 159 | 160 | // useful to detect only if there's no MutationObserver 161 | DOMAttrModified = MutationObserver || function() { 162 | doesNotSupportDOMAttrModified = false; 163 | documentElement.removeEventListener( 164 | DOM_ATTR_MODIFIED, 165 | DOMAttrModified 166 | ); 167 | }, 168 | 169 | // internal flags 170 | setListener = false, 171 | doesNotSupportDOMAttrModified = true, 172 | 173 | // optionally defined later on 174 | onSubtreeModified, 175 | callDOMAttrModified, 176 | getAttributesMirror, 177 | observer, 178 | 179 | // based on setting prototype capability 180 | // will check proto or the expando attribute 181 | // in order to setup the node once 182 | patchIfNotAlready, 183 | patch; 184 | 185 | if (sPO || hasProto) { 186 | patchIfNotAlready = function(node, proto) { 187 | if (!iPO.call(proto, node)) { 188 | /* jshint -W003 */ 189 | setupNode(node, proto); 190 | } 191 | }; 192 | /* jshint -W003 */ 193 | patch = setupNode; 194 | } else { 195 | patchIfNotAlready = function(node, proto) { 196 | if (!node[EXPANDO_UID]) { 197 | node[EXPANDO_UID] = Object(true); 198 | setupNode(node, proto); 199 | } 200 | }; 201 | patch = patchIfNotAlready; 202 | } 203 | 204 | if (!MutationObserver) { 205 | documentElement.addEventListener(DOM_ATTR_MODIFIED, DOMAttrModified); 206 | documentElement.setAttribute(EXPANDO_UID, 1); 207 | documentElement.removeAttribute(EXPANDO_UID); 208 | if (doesNotSupportDOMAttrModified) { 209 | onSubtreeModified = function(e) { 210 | var 211 | node = this, 212 | oldAttributes, 213 | newAttributes, 214 | key; 215 | if (node === e.target) { 216 | oldAttributes = node[EXPANDO_UID]; 217 | node[EXPANDO_UID] = (newAttributes = getAttributesMirror(node)); 218 | /* jshint -W033 */ 219 | for (key in newAttributes) { 220 | if (!(key in oldAttributes)) { 221 | // attribute was added 222 | return callDOMAttrModified( 223 | 0, 224 | node, 225 | key, 226 | oldAttributes[key], 227 | newAttributes[key], 228 | 'ADDITION' 229 | ); 230 | } else if (newAttributes[key] !== oldAttributes[key]) { 231 | // attribute was changed 232 | return callDOMAttrModified( 233 | 1, 234 | node, 235 | key, 236 | oldAttributes[key], 237 | newAttributes[key], 238 | 'MODIFICATION' 239 | ); 240 | } 241 | } 242 | // checking if it has been removed 243 | for (key in oldAttributes) { 244 | if (!(key in newAttributes)) { 245 | // attribute removed 246 | return callDOMAttrModified( 247 | 2, 248 | node, 249 | key, 250 | oldAttributes[key], 251 | newAttributes[key], 252 | 'REMOVAL' 253 | ); 254 | } 255 | } 256 | } 257 | }; 258 | callDOMAttrModified = function( 259 | attrChange, 260 | currentTarget, 261 | attrName, 262 | prevValue, 263 | newValue, 264 | action 265 | ) { 266 | var e = { 267 | attrChange: attrChange, 268 | currentTarget: currentTarget, 269 | attrName: attrName, 270 | prevValue: prevValue, 271 | newValue: newValue 272 | }; 273 | e[action] = attrChange; 274 | onDOMAttrModified(e); 275 | }; 276 | getAttributesMirror = function(node) { 277 | for (var 278 | attr, name, 279 | result = {}, 280 | attributes = node.attributes, 281 | i = 0, length = attributes.length; i < length; i++) { 282 | attr = attributes[i]; 283 | name = attr.name; 284 | if (name !== 'setAttribute') { 285 | result[name] = attr.value; 286 | } 287 | } 288 | return result; 289 | }; 290 | } 291 | } 292 | 293 | function loopAndVerify(list, action) { 294 | for (var i = 0, length = list.length; i < length; i++) { 295 | verifyAndSetupAndAction(list[i], action); 296 | } 297 | } 298 | 299 | function loopAndSetup(list) { 300 | for (var i = 0, length = list.length, node; i < length; i++) { 301 | node = list[i]; 302 | patch(node, protos[getTypeIndex(node)]); 303 | } 304 | } 305 | 306 | function executeAction(action) { 307 | return function(node) { 308 | if (iPO.call(HTMLElementPrototype, node)) { 309 | verifyAndSetupAndAction(node, action); 310 | loopAndVerify( 311 | node.querySelectorAll(query), 312 | action 313 | ); 314 | } 315 | }; 316 | } 317 | 318 | function getTypeIndex(target) { 319 | var 320 | is = target.getAttribute('is'), 321 | nodeName = target.nodeName, 322 | i = indexOf.call( 323 | types, 324 | is ? 325 | PREFIX_IS + is.toUpperCase() : 326 | PREFIX_TAG + nodeName 327 | ); 328 | return is && -1 < i && !isInQSA(nodeName, is) ? -1 : i; 329 | } 330 | 331 | function isInQSA(name, type) { 332 | return -1 < query.indexOf(name + '[is="' + type + '"]'); 333 | } 334 | 335 | function onDOMAttrModified(e) { 336 | var 337 | node = e.currentTarget, 338 | attrChange = e.attrChange, 339 | prevValue = e.prevValue, 340 | newValue = e.newValue; 341 | if (node.attributeChangedCallback && 342 | e.attrName !== 'style') { 343 | node.attributeChangedCallback( 344 | e.attrName, 345 | attrChange === e.ADDITION ? null : prevValue, 346 | attrChange === e.REMOVAL ? null : newValue 347 | ); 348 | } 349 | } 350 | 351 | function onDOMNode(action) { 352 | var executor = executeAction(action); 353 | return function(e) { 354 | executor(e.target); 355 | }; 356 | } 357 | 358 | function patchedSetAttribute(name, value) { 359 | /* jshint -W084, -W040 */ 360 | var self = this; 361 | setAttribute.call(self, name, value); 362 | onSubtreeModified.call(self, { 363 | target: self 364 | }); 365 | } 366 | 367 | 368 | /* jshint -W003, -W040 */ 369 | function setupNode(node, proto) { 370 | setPrototype(node, proto); 371 | if (observer) { 372 | observer.observe(node, attributesObserver); 373 | } else { 374 | if (doesNotSupportDOMAttrModified) { 375 | node.setAttribute = patchedSetAttribute; 376 | node[EXPANDO_UID] = getAttributesMirror(node); 377 | node.addEventListener(DOM_SUBTREE_MODIFIED, onSubtreeModified); 378 | } 379 | node.addEventListener(DOM_ATTR_MODIFIED, onDOMAttrModified); 380 | } 381 | if (node.createdCallback) { 382 | node.created = true; 383 | node.createdCallback(); 384 | node.created = false; 385 | } 386 | } 387 | 388 | function verifyAndSetupAndAction(node, action) { 389 | var 390 | fn, 391 | i = getTypeIndex(node), 392 | attached = 'attached', 393 | detached = 'detached'; 394 | if (-1 < i) { 395 | patchIfNotAlready(node, protos[i]); 396 | i = 0; 397 | if (action === attached && !node[attached]) { 398 | node[detached] = false; 399 | node[attached] = true; 400 | i = 1; 401 | } else if (action === detached && !node[detached]) { 402 | node[attached] = false; 403 | node[detached] = true; 404 | i = 1; 405 | } 406 | if (i && (fn = node[action + 'Callback'])) fn.call(node); 407 | } 408 | } 409 | 410 | // set as enumerable, writable and configurable 411 | document[REGISTER_ELEMENT] = function registerElement(type, options) { 412 | upperType = type.toUpperCase(); 413 | if (!setListener) { 414 | // only first time document.registerElement is used 415 | // we need to set this listener 416 | // setting it by default might slow down for no reason 417 | setListener = true; 418 | if (MutationObserver) { 419 | observer = (function(attached, detached) { 420 | function checkEmAll(list, callback) { 421 | for (var i = 0, length = list.length; i < length; callback(list[i++])) {} 422 | } 423 | return new MutationObserver(function(records) { 424 | for (var 425 | current, node, 426 | i = 0, length = records.length; i < length; i++) { 427 | current = records[i]; 428 | if (current.type === 'childList') { 429 | checkEmAll(current.addedNodes, attached); 430 | checkEmAll(current.removedNodes, detached); 431 | } else { 432 | node = current.target; 433 | if (node.attributeChangedCallback && 434 | current.attributeName !== 'style') { 435 | node.attributeChangedCallback( 436 | current.attributeName, 437 | current.oldValue, 438 | node.getAttribute(current.attributeName) 439 | ); 440 | } 441 | } 442 | } 443 | }); 444 | }(executeAction('attached'), executeAction('detached'))); 445 | observer.observe( 446 | document, { 447 | childList: true, 448 | subtree: true 449 | } 450 | ); 451 | } else { 452 | document.addEventListener('DOMNodeInserted', onDOMNode('attached')); 453 | document.addEventListener('DOMNodeRemoved', onDOMNode('detached')); 454 | } 455 | 456 | document.addEventListener('readystatechange', function() { 457 | loopAndVerify( 458 | document.querySelectorAll(query), 459 | 'attached' 460 | ); 461 | }); 462 | 463 | document.createElement = function(localName, typeExtension) { 464 | var 465 | node = createElement.apply(document, arguments), 466 | i = indexOf.call( 467 | types, (typeExtension ? PREFIX_IS : PREFIX_TAG) + 468 | (typeExtension || localName).toUpperCase() 469 | ), 470 | setup = -1 < i; 471 | if (typeExtension) { 472 | node.setAttribute('is', typeExtension = typeExtension.toLowerCase()); 473 | if (setup) { 474 | setup = isInQSA(localName.toUpperCase(), typeExtension); 475 | } 476 | } 477 | if (setup) patch(node, protos[i]); 478 | return node; 479 | }; 480 | 481 | HTMLElementPrototype.cloneNode = function(deep) { 482 | var 483 | node = cloneNode.call(this, !!deep), 484 | i = getTypeIndex(node); 485 | if (-1 < i) patch(node, protos[i]); 486 | if (deep) loopAndSetup(node.querySelectorAll(query)); 487 | return node; 488 | }; 489 | } 490 | 491 | if (-2 < ( 492 | indexOf.call(types, PREFIX_IS + upperType) + 493 | indexOf.call(types, PREFIX_TAG + upperType) 494 | )) { 495 | throw new Error('A ' + type + ' type is already registered'); 496 | } 497 | 498 | if (!validName.test(upperType) || -1 < indexOf.call(invalidNames, upperType)) { 499 | throw new Error('The type ' + type + ' is invalid'); 500 | } 501 | 502 | var 503 | constructor = function() { 504 | return document.createElement(nodeName, extending && upperType); 505 | }, 506 | opt = options || OP, 507 | extending = hOP.call(opt, EXTENDS), 508 | nodeName = extending ? options[EXTENDS].toUpperCase() : upperType, 509 | /* jshint -W003 */ 510 | i = types.push((extending ? PREFIX_IS : PREFIX_TAG) + upperType) - 1, 511 | upperType; 512 | 513 | query = query.concat( 514 | query.length ? ',' : '', 515 | extending ? nodeName + '[is="' + type.toLowerCase() + '"]' : nodeName 516 | ); 517 | 518 | constructor.prototype = ( 519 | protos[i] = hOP.call(opt, 'prototype') ? 520 | opt.prototype : 521 | create(HTMLElementPrototype) 522 | ); 523 | 524 | loopAndVerify( 525 | document.querySelectorAll(query), 526 | 'attached' 527 | ); 528 | 529 | return constructor; 530 | }; 531 | 532 | 533 | }(window, document, Object, 'registerElement')); -------------------------------------------------------------------------------- /prototype/fill.js: -------------------------------------------------------------------------------- 1 | /* global window,document,console */ 2 | (function() { 3 | window.MediaQueryMappings = { 4 | cache: [], 5 | queue: [], 6 | recalc: function () { 7 | var mediaList; 8 | MediaQueryMappings.cache.forEach(function (o) { 9 | MediaQueryMappings.queue.push(o); 10 | mediaList = window.matchMedia(o.mediaQuery); 11 | //console.log("matched %s for preferredDisplayMode %s", o.mediaQuery, o.preferredDisplayMode); 12 | o.mode = (mediaList.matches) ? "add" : "remove"; 13 | throttleManageClass(o.selector, o.preferredDisplayMode, o.mode); 14 | }); 15 | }, 16 | add: function(o) { 17 | var listener = function(evt) { 18 | //console.log("matched %s for preferredDisplayMode %s", o.mediaQuery, o.preferredDisplayMode); 19 | o.mode = (evt.matches) ? "add" : "remove"; 20 | MediaQueryMappings.queue.push(o); 21 | MediaQueryMappings.cache.push(o); 22 | throttleManageClass(o.selector, o.preferredDisplayMode, o.mode); 23 | }; 24 | var mediaList = window.matchMedia(o.mediaQuery); 25 | mediaList.addListener(listener); 26 | listener(mediaList); /* already has matches */ 27 | } 28 | }; 29 | 30 | /** 31 | * Helper functions for Selector specificity 32 | * @type {Object} 33 | */ 34 | var specificity = { 35 | expressions: function() { 36 | var regExs = { 37 | pre: /\([^\)]+\)/, 38 | ids: /#[\d\w\-_]+/g, 39 | cls: /[\.:\[][^\.:\[+>]+/g, 40 | tag: /(^|[\s\+>])\w+/g 41 | }; 42 | regExs.chop = [regExs.ids, regExs.cls, regExs.tag]; 43 | return regExs; 44 | }, 45 | /** 46 | * Calculates the specificity of a Selector 47 | * @return {Number} An hexadecimal value 48 | */ 49 | calculate: function(selector) { 50 | var expressions = specificity.expressions(); 51 | var s = selector.replace(expressions.pre, ""); 52 | return parseInt(expressions.chop.map(function(p) { 53 | var m = s.match(p); 54 | return m ? m.length.toString(16) : 0; 55 | }).join(""), 16); 56 | } 57 | }; 58 | 59 | var throttleManageClassId; 60 | var throttleManageClass = function() { 61 | if (throttleManageClassId) { 62 | window.clearTimeout(throttleManageClassId); 63 | } 64 | throttleManageClassId = setTimeout(function() { 65 | MediaQueryMappings.queue.sort(function(a, b) { 66 | return a.specificity > b.specificity ? 1 : (a.specificity < b.specificity) ? -1 : 0; 67 | }); 68 | MediaQueryMappings.queue.forEach(function(o) { 69 | manageClass(o.selector, o.preferredDisplayMode, o.mode); 70 | }); 71 | MediaQueryMappings.queue = []; 72 | }); 73 | }; 74 | // This approach doesn't deal with selector specificity, it's basically document order which means you could wind up with the 75 | // wrong display type 76 | var manageClass = function(selector, preferredDisplayMode, switcher) { 77 | Array.prototype.slice.call(document.querySelectorAll(selector)).forEach(function(el) { 78 | //el.classList[op](clazz); 79 | if (switcher === "add") { 80 | el.setAttribute("preferred-display", preferredDisplayMode, "secret"); 81 | } else if (el.getAttribute("preferred-display") === preferredDisplayMode) { 82 | el.removeAttribute("preferred-display", "secret"); 83 | } 84 | }); 85 | }; 86 | 87 | 88 | /* Super minimal, not terribly efficent API for getting at the things you want */ 89 | var SimpleScanner = function(sv) { 90 | var currentIndex = 0, 91 | s = sv; 92 | this.context = {}; 93 | this.hasMore = function() { 94 | //console.log("%d in %d", currentIndex, (s.length - 1)); 95 | return currentIndex < s.length - 1; 96 | }; 97 | this.skipUntil = function(sub, consume) { 98 | var remaining = s.substring(currentIndex); 99 | var offset = remaining.search(sub); 100 | if (offset == -1) { 101 | throw new Error("Unable to find \"" + sub + "\" in \"" + remaining + "\""); 102 | } 103 | if (consume) { 104 | currentIndex += offset + remaining.match(sub)[0].length; 105 | } 106 | return this; 107 | }; 108 | this.captureUntil = function(sub, propName) { 109 | // Damn you RegExs 110 | var remaining = s.substring(currentIndex); 111 | var end = remaining.search(sub), 112 | rst; 113 | if (end === -1) { 114 | throw new Error("Unable to find \"" + sub + "\" in \"" + remaining + "\""); 115 | } 116 | end += currentIndex; 117 | rst = s.substring(currentIndex, end); 118 | currentIndex = end + 1; 119 | this.context[propName] = rst.trim(); 120 | return this; 121 | }; 122 | this.clearContext = function() { 123 | this.context = {}; 124 | }; 125 | }; 126 | 127 | window.addEventListener("DOMContentLoaded", function() { 128 | var rules = []; 129 | /* this represents us getting source for this from somewhere...*/ 130 | var srcEl = document.querySelector("[type=\"text/MediaQueryMappings\"]"); 131 | var src = (srcEl) ? srcEl.innerHTML : ""; 132 | src.split("@media").forEach(function(chunk) { 133 | 134 | var scanner = new SimpleScanner(chunk); 135 | try { 136 | while (scanner.hasMore()) { 137 | ((scanner.context.query) ? scanner : scanner.captureUntil("{", "query")) 138 | .captureUntil("{", "selector") 139 | .skipUntil("--preferred-display:", true) 140 | .captureUntil(";", "preferredDisplayMode"). 141 | skipUntil("}", true). 142 | skipUntil(/\S/); 143 | 144 | 145 | /* I can grab those parsed things from the context */ 146 | rules.push({ 147 | mediaQuery: scanner.context.query, 148 | selector: scanner.context.selector, 149 | specificity: specificity.calculate(scanner.context.selector), 150 | preferredDisplayMode: scanner.context.preferredDisplayMode 151 | }); 152 | scanner.context = { 153 | query: scanner.context.query 154 | }; 155 | } 156 | } catch (e) { 157 | console.error(e); 158 | } 159 | }); 160 | 161 | 162 | /* This is a problem... we have to wait for DCL if necessary and then search... upgrading should allow a check.. */ 163 | //window.addEventListener("DOMContentLoaded", function () { 164 | rules.forEach(function(rule) { 165 | //console.info(rule); 166 | MediaQueryMappings.add(rule); 167 | }); 168 | }, false); 169 | }()); -------------------------------------------------------------------------------- /prototype/mini.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom Elements and Opera Mini 5 | 6 | 7 | 18 | 19 | 20 |
21 |

22 | This is a simple test of a custom element to see if it works in Opera mini and, if not, figure out why... 23 | The box below will say either "IT WORKED!" or "IT DIDN'T WORK :( Why?!". 24 | 25 | IT DIDN'T WORK :( Why?! 26 | 27 | 28 |

29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /prototype/panelset-element-tabs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Panel and Panel Sets Prototypes - tabs 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |

Panel Set's preferred-display Attribute (tabset)

32 |

This example demonstrates the use of a preferred-display attribute set to tabset on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

33 | <common-panel-set preferred-display="tabset">
34 |     <common-panel>
35 |        <common-panel-title>Local</common-panel-title>
36 |        <p>This would contain content about local news stories.</p>
37 |     </common-panel>
38 |
39 |     <common-panel>
40 |        <common-panel-title>Entertainment</common-panel-title>
41 |        <p>This would contain content about entertainment news stories.</p>
42 |     </common-panel>
43 |
44 |     <common-panel>
45 |        <common-panel-title>Sports</common-panel-title>
46 |        <p>This would contain content about sports stories.</p>
47 |     </common-panel>
48 | </common-panel-set>
49 |
50 |

Demo

51 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 52 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 53 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 54 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 55 | It is functionally identical to the previous example, only the visual representation should change.

56 | 57 | 58 | Local 59 |

This would contain content about local news stories.

60 |
61 | 62 | 63 | Entertainment 64 |

This would contain content about entertainment news stories.

65 |
66 | 67 | 68 | Sports 69 |

This would contain content about sports stories.

70 |
71 |
72 |
73 |
74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /prototype/panelset-element.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Panel and Panel Sets Prototypes 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 |

Panel and Panel Sets Prototypes

48 |

This page contains descriptions, examples and instances of prototype implemention (prollyfill) of 49 | the Panels and Panel Sets Draft. It can be used 50 | for illustration and education, as well as serve as a host for tests for manual or automated 51 | checking of conformance for the prollyfill itself.

52 | 53 |
54 |

Panels

55 |
56 |

Basic Panel

57 |

This example demonstrates use of a simple, basic common-panel.

58 | <common-panel>
59 |   <common-panel-title>My favorite books</common-panel-title>
60 |   <p>Here are a list of my favorite books:
61 |     <ol>
62 |       <li>Hitchhiker's Guide to the Galaxy</li>
63 |       <li>Pride and Prejudice</li>
64 |       <li>Green Eggs and Ham</li>
65 |     </ol>
66 |   </p>
67 | </common-panel> 68 |
69 |
70 |

Demo

71 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel with 72 | a title "My favorite books" and paragraph with a list of three of one of the author's favorites. A user 73 | should be able to switch from the title to content by way of the TAB key.

74 | 75 | My favorite books 76 |

Here are a list of my favorite books: 77 |

    78 |
  1. Hitchhiker's Guide to the Galaxy
  2. 79 |
  3. Pride and Prejudice
  4. 80 |
  5. Green Eggs and Ham
  6. 81 |
82 |

83 |
84 |
85 |
86 | 87 |
88 |

Removable Panel

89 |

This example demonstrates use of a common-panel with the is-removable attribute set.

90 | <common-panel is-removable>
91 |   <common-panel-title>Reminder</common-panel-title>
92 |   <p>You should try using this with assistive technology and, if you are sighted, close your eyes.</p>
93 | </common-panel> 94 |
95 |
96 |

Demo

97 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel with 98 | a title "Reminder" and, as part of the title, "X" button which can be used to remove it. It should also contain content 99 | (the reminder) which is a one sentence paragraph about testing. Users should be able to TAB focus onto the title 100 | content, TAB again to the close button. Pressing spacebar should cause this panel to be removed from the 101 | document in its entirety. If not removed, TAB again should switch focus to the content. 102 |

103 | 104 | Reminder 105 |

You should try using this with assistive technology and, if you are sighted, close your eyes.

106 |
107 |
108 |
109 | 110 |
111 |

Expandable/Collapsable Panel

112 |

This example demonstrates the use of a common-panel with the a specified expansion-state attribute 113 | set to "opened".

114 | <common-panel expansion-state="opened">
115 |   <common-panel-title>Today's News</common-panel-title>
116 |   <p>In today's news, accessibility matters. Accessibility first design might actually help you realize some things that improve your design for visually oriented users as well, for example, this whole proposal!</p>
117 | </common-panel>
118 |
119 |

Demo

120 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel with the 121 | an expansion state of opened. The user should be able to TAB focus onto the title content and, when the panel is 122 | opened (as it is by default here) pressing the TAB should move focus to the content: a paragraph about acessibilty first design. If the user presses the spacebar on the title, as it is collapsable, this should cause the content to become hidden and removed from 123 | TAB navigation until it is re-opened.

124 | 125 | Today's News 126 |

In today's news, accessibility matters. Accessibility first design might actually help you realize some things that improve your 127 | design for visually oriented users as well, for example, this whole proposal!

128 |
129 |
130 |
131 |
132 | 133 |
134 |

Panel Sets

135 |
136 |

Simple Panel Set

137 |

This exaple demonstrates the use of common-panel-set surrounding a collection of simple, vanilla panels about 138 | themeatic groups of news stories. The panel set makes them all inherently "expandable" and manages their simple selection. 139 | As none claims to be expanded, the first one is expanded.

140 | <common-panel-set>
141 |     <common-panel>
142 |        <common-panel-title>Local</common-panel-title>
143 |        <p>This would contain content about local news stories.</p>
144 |     </common-panel>
145 |
146 |     <common-panel>
147 |        <common-panel-title>Entertainment</common-panel-title>
148 |        <p>This would contain content about entertainment news stories.</p>
149 |     </common-panel>
150 |
151 |     <common-panel>
152 |        <common-panel-title>Sports</common-panel-title>
153 |        <p>This would contain content about sports stories.</p>
154 |     </common-panel>
155 | </common-panel-set>
156 |
157 |

Demo

158 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 159 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 160 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 161 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set.

162 | 163 | 164 | Local 165 |

This would contain content about local news stories.

166 |
167 | 168 | 169 | Entertainment 170 |

This would contain content about entertainment news stories.

171 |
172 | 173 | 174 | Sports 175 |

This would contain content about sports stories.

176 |
177 |
178 |
179 |
180 | 181 |
182 |

Panel Set (explicit expansion)

183 |

This exaple demonstrates the use of common-panel-set surrounding a collection of panels about 184 | themeatic groups of news stories. As common-panel children claim their expansion state to be "opened", 185 | each is processed in document order and the common-panel-set closes others. This means, in a manner 186 | similar to the select element, the last one in document order to make the claim ultimately "wins".

187 | <common-panel-set>
188 |     <common-panel >
189 |        <common-panel-title>Local</common-panel-title>
190 |        <p>This would contain content about local news stories.</p>
191 |     </common-panel>
192 |
193 |     <common-panel expansion-state="opened">
194 |        <common-panel-title>Entertainment</common-panel-title>
195 |        <p>This would contain content about entertainment news stories.</p>
196 |     </common-panel>
197 |
198 |     <common-panel expansion-state="opened"
199 |        <common-panel-title>Sports</common-panel-title>
200 |        <p>This would contain content about sports stories.</p>
201 |     </common-panel>
202 | </common-panel-set>
203 |
204 |

Demo

205 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 206 | two of the common-panel children specify an expansion state of opened, thus, the last one should be. 207 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 208 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 209 | In all ways beyond which panel is initially expanded, this should be functionally identical to the previous example.

210 | 211 | 212 | Local 213 |

This would contain content about local news stories.

214 |
215 | 216 | 217 | Entertainment 218 |

This would contain content about entertainment news stories.

219 |
220 | 221 | 222 | Sports 223 |

This would contain content about sports stories.

224 |
225 |
226 |
227 |
228 | 229 |
230 |

Panel Set's preferred-display Attribute (tabset)

231 |

This example demonstrates the use of a preferred-display attribute set to tabset on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

232 | <common-panel-set preferred-display="tabset">
233 |     <common-panel>
234 |        <common-panel-title>Local</common-panel-title>
235 |        <p>This would contain content about local news stories.</p>
236 |     </common-panel>
237 |
238 |     <common-panel>
239 |        <common-panel-title>Entertainment</common-panel-title>
240 |        <p>This would contain content about entertainment news stories.</p>
241 |     </common-panel>
242 |
243 |     <common-panel>
244 |        <common-panel-title>Sports</common-panel-title>
245 |        <p>This would contain content about sports stories.</p>
246 |     </common-panel>
247 | </common-panel-set>
248 |
249 |

Demo

250 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 251 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 252 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 253 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 254 | It is functionally identical to the previous example, only the visual representation should change.

255 | 256 | 257 | Local 258 |

This would contain content about local news stories.

259 |
260 | 261 | 262 | Entertainment 263 |

This would contain content about entertainment news stories.

264 |
265 | 266 | 267 | Sports 268 |

This would contain content about sports stories.

269 |
270 |
271 |
272 |
273 | 274 | 275 |
276 |

Panel Set's preferred-display Attribute (carousel)

277 |

This example demonstrates the use of a preferred-display attribute set to carousel on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

278 | <common-panel-set preferred-display="carousel">
279 |     <common-panel>
280 |        <common-panel-title>Local</common-panel-title>
281 |        <p>This would contain content about local news stories.</p>
282 |     </common-panel>
283 |
284 |     <common-panel">
285 |        <common-panel-title>Entertainment</common-panel-title>
286 |        <p>This would contain content about entertainment news stories.</p>
287 |     </common-panel>
288 |
289 |     <common-panel>
290 |        <common-panel-title>Sports</common-panel-title>
291 |        <p>This would contain content about sports stories.</p>
292 |     </common-panel>
293 | </common-panel-set>
294 |
295 |

Demo

296 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 297 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 298 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 299 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 300 | It is functionally identical to the previous example, only the visual representation should change.

301 | 302 | 303 | Local 304 |

This would contain content about local news stories.

305 |
306 | 307 | 308 | Entertainment 309 |

This would contain content about entertainment news stories.

310 |
311 | 312 | 313 | Sports 314 |

This would contain content about sports stories.

315 |
316 |
317 |
318 |
319 | 320 |
321 |

Panel Set's preferred-display Attribute (accordion)

322 |

This example demonstrates the use of a preferred-display attribute set to accordion on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

323 | <common-panel-set preferred-display="accordion">
324 |     <common-panel>
325 |        <common-panel-title>Local</common-panel-title>
326 |        <p>This would contain content about local news stories.</p>
327 |     </common-panel>
328 |
329 |     <common-panel>
330 |        <common-panel-title>Entertainment</common-panel-title>
331 |        <p>This would contain content about entertainment news stories.</p>
332 |     </common-panel>
333 |
334 |     <common-panel>
335 |        <common-panel-title>Sports</common-panel-title>
336 |        <p>This would contain content about sports stories.</p>
337 |     </common-panel>
338 | </common-panel-set>
339 |
340 |

Demo

341 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 342 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 343 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 344 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 345 | It is functionally identical to the previous example, only the visual representation should change.

346 | 347 | 348 | Local 349 |

This would contain content about local news stories.

350 |
351 | 352 | 353 | Entertainment 354 |

This would contain content about entertainment news stories.

355 |
356 | 357 | 358 | Sports 359 |

This would contain content about sports stories.

360 |
361 |
362 |
363 |
364 | 365 |
366 |

Panel Set's preferred-display Attribute (accordion)

367 |

This example demonstrates the use of a preferred-display attribute set to accordion on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

368 | <common-panel-set preferred-display="accordion">
369 |     <common-panel>
370 |        <common-panel-title>Local</common-panel-title>
371 |        <p>This would contain content about local news stories.</p>
372 |     </common-panel>
373 |
374 |     <common-panel>
375 |        <common-panel-title>Entertainment</common-panel-title>
376 |        <p>This would contain content about entertainment news stories.</p>
377 |     </common-panel>
378 |
379 |     <common-panel>
380 |        <common-panel-title>Sports</common-panel-title>
381 |        <p>This would contain content about sports stories.</p>
382 |     </common-panel>
383 | </common-panel-set>
384 |
385 |

Demo

386 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 387 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 388 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 389 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 390 | It is functionally identical to the previous example, only the visual representation should change.

391 | 392 | 393 | Local 394 |

This would contain content about local news stories.

395 |
396 | 397 | 398 | Entertainment 399 |

This would contain content about entertainment news stories.

400 |
401 | 402 | 403 | Sports 404 |

This would contain content about sports stories.

405 |
406 |
407 |
408 |
409 | 410 | 411 |
412 |

Controlling preferred-display via Media Queries

413 |

This example demonstrates the use of media queries to control preferred-display attribute to make it tabset on wide screen, but accordion on all others. The common-panel-set element with content identical to the previous example. Only the visual representation should change.

414 | <!-- demo uses script type="text/MediaQueryMappings" lightweight polyfill -->
415 | <style>
416 |     @media all {
417 |         #foo {
418 |             --preferred-display: accordion;
419 |         }
420 |     }
421 |
422 |     @media screen and (min-width: 767px) {
423 |         #foo {
424 |             --preferred-display: tabset;
425 |         }
426 |      }
427 | </style>
428 | 429 | <common-panel-set id="foo">
430 |     <common-panel>
431 |        <common-panel-title>Local</common-panel-title>
432 |        <p>This would contain content about local news stories.</p>
433 |     </common-panel>
434 |
435 |     <common-panel>
436 |        <common-panel-title>Entertainment</common-panel-title>
437 |        <p>This would contain content about entertainment news stories.</p>
438 |     </common-panel>
439 |
440 |     <common-panel>
441 |        <common-panel-title>Sports</common-panel-title>
442 |        <p>This would contain content about sports stories.</p>
443 |     </common-panel>
444 | </common-panel-set>
445 |
446 |

Demo

447 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 448 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 449 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 450 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 451 | Note that as its representation changes, neither the panel selection, nor the tab-focus should be lost. 452 | It is functionally identical to the previous example, only the visual representation should change.

453 | 454 | 455 | Local 456 |

This would contain content about local news stories.

457 |
458 | 459 | 460 | Entertainment 461 |

This would contain content about entertainment news stories.

462 |
463 | 464 | 465 | Sports 466 |

This would contain content about sports stories.

467 |
468 |
469 |
470 |
471 |
472 | 473 |
474 | 475 | 476 | 477 | 478 | 479 | -------------------------------------------------------------------------------- /prototype/panelset.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | /*! 3 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome 4 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 5 | */ 6 | /* FONT PATH 7 | * -------------------------- */ 8 | @font-face { 9 | font-family: 'FontAwesome'; 10 | src: url('../fonts/fontawesome-webfont.eot?v=4.1.0'); 11 | src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | 16 | common-panel-set { 17 | display: block; 18 | margin-bottom: 20px; 19 | } 20 | common-panel-set .common-panel-header { 21 | cursor: pointer; 22 | } 23 | common-panel { 24 | display: block; 25 | position: relative; 26 | font-size: 13px; 27 | font-family: Arial, Helvetica, sans-serif; 28 | margin: 0; 29 | padding: 0; 30 | color: #000000; 31 | background-color: #ffffff; 32 | -webkit-border-radius: 0; 33 | -moz-border-radius: 0; 34 | -ms-border-radius: 0; 35 | -o-border-radius: 0; 36 | border-radius: 0; 37 | border: 1px solid #cccccc; 38 | border: 1px solid rgba(0, 0, 0, 0.3); 39 | -webkit-box-shadow: 0 1px 2px 1px #cccccc; 40 | -moz-box-shadow: 0 1px 2px 1px #cccccc; 41 | box-shadow: 0 1px 2px 1px #cccccc; 42 | -webkit-box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.175); 43 | -moz-box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.175); 44 | box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.175); 45 | } 46 | common-panel:before, 47 | common-panel:after { 48 | content: " "; 49 | display: table; 50 | } 51 | common-panel:after { 52 | clear: both; 53 | } 54 | common-panel .common-panel-header { 55 | font-size: 16px; 56 | color: #000000; 57 | font-weight: 700; 58 | line-height: 20px; 59 | padding: 10px 15px 10px 15px; 60 | margin: 0; 61 | background-color: #f7f7f7; 62 | -webkit-border-radius: 0 0 0 0; 63 | -moz-border-radius: 0 0 0 0; 64 | -ms-border-radius: 0 0 0 0; 65 | -o-border-radius: 0 0 0 0; 66 | border-radius: 0 0 0 0; 67 | border-bottom: 1px solid #bbbbbb; 68 | } 69 | common-panel > .common-panel-content { 70 | margin: 0; 71 | padding: 0; 72 | } 73 | common-panel .common-panel-remove { 74 | -webkit-text-shadow: 1px 1px 1px #ffffff; 75 | -moz-text-shadow: 1px 1px 1px #ffffff; 76 | -ms-text-shadow: 1px 1px 1px #ffffff; 77 | -o-text-shadow: 1px 1px 1px #ffffff; 78 | text-shadow: 1px 1px 1px #ffffff; 79 | opacity: 0.5; 80 | filter: alpha(opacity=50); 81 | font-family: Roboto, arial, sans-serif; 82 | font-size: 20px; 83 | position: absolute; 84 | top: 10px; 85 | right: 4px; 86 | line-height: 20px; 87 | font-weight: bold; 88 | border: 0; 89 | color: #000000; 90 | background-color: transparent; 91 | } 92 | common-panel .common-panel-remove:hover { 93 | opacity: 0.9; 94 | filter: alpha(opacity=90); 95 | } 96 | common-panel[expansion-state]:not([expansion-state="opened"]) > *:not(.common-panel-header-box) { 97 | display: none; 98 | } 99 | common-panel[expansion-state] > .common-panel-header-box > .common-panel-header > span::after { 100 | float: right; 101 | content: "\25BC"; 102 | font-size: 14px; 103 | } 104 | common-panel[expansion-state="opened"] > .common-panel-header-box > .common-panel-header > span::after { 105 | float: right; 106 | content: "\25B2"; 107 | font-size: 14px; 108 | } 109 | common-panel.borderless { 110 | background-color: transparent; 111 | -webkit-border-radius: 0; 112 | -moz-border-radius: 0; 113 | -ms-border-radius: 0; 114 | -o-border-radius: 0; 115 | border-radius: 0; 116 | border: 0 solid #cccccc; 117 | -webkit-box-shadow: none; 118 | -moz-box-shadow: none; 119 | box-shadow: none; 120 | } 121 | common-panel.borderless .common-panel-header { 122 | padding: 10px 0 10px 0; 123 | margin: 0; 124 | background-color: transparent; 125 | -webkit-border-radius: 0; 126 | -moz-border-radius: 0; 127 | -ms-border-radius: 0; 128 | -o-border-radius: 0; 129 | border-radius: 0; 130 | border-bottom: 1px solid #bbbbbb; 131 | } 132 | common-panel[expansion-state="closed"] > .common-panel-header-box > .common-panel-header { 133 | border-bottom: 0; 134 | -webkit-border-radius: 0; 135 | -moz-border-radius: 0; 136 | -ms-border-radius: 0; 137 | -o-border-radius: 0; 138 | border-radius: 0; 139 | } 140 | common-panel.minus-plus-panel[expansion-state] > .common-panel-header-box > .common-panel-header > span::after { 141 | content: "\f067"; 142 | } 143 | common-panel.minus-plus-panel[expansion-state="opened"] > .common-panel-header-box > .common-panel-header > span::after { 144 | content: "\f068"; 145 | } 146 | common-panel[expansion-state="closed"] .common-panel-header { 147 | background-color: #f0f0f0; 148 | margin: 0; 149 | color: #000000; 150 | } 151 | common-panel[expansion-state="opened"] .common-panel-header { 152 | background-color: #f7f7f7; 153 | margin: 0; 154 | color: #000000; 155 | } 156 | .common-panel-tabs { 157 | *zoom: 1; 158 | border-bottom: 1px solid #dddddd; 159 | font-size: 13px; 160 | font-family: Arial, Helvetica, sans-serif; 161 | color: #000000; 162 | background-color: transparent; 163 | -webkit-border-radius: 0 0 0 0; 164 | -moz-border-radius: 0 0 0 0; 165 | -ms-border-radius: 0 0 0 0; 166 | -o-border-radius: 0 0 0 0; 167 | border-radius: 0 0 0 0; 168 | } 169 | .common-panel-tabs::before, 170 | .common-panel-tabs::after { 171 | display: table; 172 | line-height: 0; 173 | content: ""; 174 | clear: both; 175 | } 176 | .common-panel-tabs .common-panel-header { 177 | float: left; 178 | margin-bottom: -1px; 179 | border: 1px solid transparent; 180 | line-height: 20px; 181 | font-weight: 400; 182 | margin-right: 2px; 183 | padding: 8px 12px; 184 | border-right-color: transparent; 185 | -webkit-border-radius: 0 0 0 0; 186 | -moz-border-radius: 0 0 0 0; 187 | -ms-border-radius: 0 0 0 0; 188 | -o-border-radius: 0 0 0 0; 189 | border-radius: 0 0 0 0; 190 | } 191 | .common-panel-tabs .common-panel-header:hover, 192 | .common-panel-tabs .common-panel-header:focus { 193 | background-color: #eeeeee; 194 | border-color: #dddddd #dddddd #dddddd #dddddd; 195 | } 196 | .common-panel-tabs .common-panel-header[aria-selected], 197 | .common-panel-tabs .common-panel-header[aria-selected]:hover, 198 | .common-panel-tabs .common-panel-header[aria-selected]:focus { 199 | background-color: #ffffff; 200 | border-color: #dddddd #dddddd transparent; 201 | border-style: solid; 202 | border-width: 1px; 203 | color: #00324b; 204 | -webkit-border-radius: 0 0 0 0; 205 | -moz-border-radius: 0 0 0 0; 206 | -ms-border-radius: 0 0 0 0; 207 | -o-border-radius: 0 0 0 0; 208 | border-radius: 0 0 0 0; 209 | } 210 | .common-panel-tabs .common-panel-header .common-panel-remove { 211 | -webkit-text-shadow: 1px 1px 1px #ffffff; 212 | -moz-text-shadow: 1px 1px 1px #ffffff; 213 | -ms-text-shadow: 1px 1px 1px #ffffff; 214 | -o-text-shadow: 1px 1px 1px #ffffff; 215 | text-shadow: 1px 1px 1px #ffffff; 216 | opacity: 0.5; 217 | filter: alpha(opacity=50); 218 | font-family: Roboto, arial, sans-serif; 219 | font-size: 20px; 220 | position: absolute; 221 | top: 10px; 222 | right: 4px; 223 | line-height: 20px; 224 | font-weight: bold; 225 | border: 0; 226 | color: #000000; 227 | background-color: transparent; 228 | } 229 | .common-panel-tabs .common-panel-header .common-panel-remove:hover { 230 | opacity: 0.9; 231 | filter: alpha(opacity=90); 232 | } 233 | common-panel-set[preferred-display="tabset"] > common-panel { 234 | border-top: 0; 235 | } 236 | common-panel-set[preferred-display="tabset"] > common-panel > .common-panel-header-box > .common-panel-header { 237 | display: none; 238 | } 239 | common-panel-set[preferred-display="tabset"] > common-panel:not([expansion-state="opened"]) { 240 | display: none; 241 | } 242 | common-panel-set[preferred-display="tabset"] > common-panel { 243 | -webkit-border-radius: 0 0 0 0; 244 | -moz-border-radius: 0 0 0 0; 245 | -ms-border-radius: 0 0 0 0; 246 | -o-border-radius: 0 0 0 0; 247 | border-radius: 0 0 0 0; 248 | } 249 | common-panel-set:not([preferred-display]) .common-panel-header, 250 | common-panel-set[preferred-display="accordion"] .common-panel-header { 251 | background-color: #f0f0f0; 252 | margin: 0; 253 | } 254 | common-panel-set:not([preferred-display]) .common-panel-header span, 255 | common-panel-set[preferred-display="accordion"] .common-panel-header span { 256 | overflow: hidden; 257 | display: block; 258 | -webkit-font-smoothing: antialiased; 259 | -moz-osx-font-smoothing: grayscale; 260 | } 261 | common-panel-set:not([preferred-display]) .common-panel-header span::before, 262 | common-panel-set[preferred-display="accordion"] .common-panel-header span::before { 263 | display: inline-block; 264 | font-style: normal; 265 | font-weight: normal; 266 | /*content: "\f078"; 267 | font-family: FontAwesome;*/ 268 | line-height: 1; 269 | font-size: 14px; 270 | width: 12px; 271 | text-align: center; 272 | } 273 | common-panel-set:not([preferred-display]) .common-panel-header:hover, 274 | common-panel-set[preferred-display="accordion"] .common-panel-header:hover { 275 | text-decoration: none; 276 | } 277 | common-panel-set:not([preferred-display]) .common-panel-tabs, 278 | common-panel-set[preferred-display="accordion"] .common-panel-tabs { 279 | display: none; 280 | } 281 | common-panel-set:not([preferred-display]) > common-panel:not(:last-child), 282 | common-panel-set[preferred-display="accordion"] > common-panel:not(:last-child) { 283 | margin-bottom: -1px; 284 | } 285 | common-panel-set:not([preferred-display]) > common-panel > .common-panel-header span::before, 286 | common-panel-set[preferred-display="accordion"] > common-panel > .common-panel-header span::before { 287 | display: none; 288 | } 289 | common-panel-set:not([preferred-display]) > common-panel[expansion-state="opened"] .common-panel-header, 290 | common-panel-set[preferred-display="accordion"] > common-panel[expansion-state="opened"] .common-panel-header { 291 | background-color: #f7f7f7; 292 | color: #000000; 293 | } 294 | common-panel-set[preferred-display="carousel"] { 295 | position: relative; 296 | } 297 | common-panel-set[preferred-display="carousel"] > common-panel { 298 | min-height: 200px; 299 | } 300 | common-panel-set[preferred-display="carousel"] > common-panel .common-panel-header { 301 | background-color: transparent; 302 | border-bottom: 0; 303 | color: #000000; 304 | } 305 | common-panel-set[preferred-display="carousel"] > common-panel > .common-panel-header-box > .common-panel-header span::after { 306 | display: none; 307 | } 308 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs { 309 | display: block; 310 | position: absolute; 311 | z-index: 1; 312 | bottom: 5px; 313 | left: 40%; 314 | right: 40%; 315 | border: 0; 316 | background-color: transparent; 317 | } 318 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header { 319 | background-color: #ccc; 320 | border: 2px solid #aaa; 321 | height: 6px; 322 | margin-right: 2px; 323 | -webkit-border-radius: 14px; 324 | -moz-border-radius: 14px; 325 | -ms-border-radius: 14px; 326 | -o-border-radius: 14px; 327 | border-radius: 14px; 328 | } 329 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header:hover, 330 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header:focus { 331 | background-color: inherit; 332 | border-color: inherit; 333 | } 334 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header[aria-selected], 335 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header[aria-selected]:hover, 336 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header[aria-selected]:focus { 337 | background-color: #fff; 338 | border: 2px solid #000; 339 | } 340 | common-panel-set[preferred-display="carousel"] > common-panel:not([expansion-state="opened"]) { 341 | display: none; 342 | } 343 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > * { 344 | display: inline; 345 | } 346 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header-box > .common-panel-header > span { 347 | display: none; 348 | } 349 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs .common-panel-remove { 350 | display: none; 351 | } 352 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header { 353 | padding: 8px 12px; 354 | } 355 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header[aria-selected], 356 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header[aria-selected]:hover, 357 | common-panel-set[preferred-display="carousel"] > .common-panel-tabs > .common-panel-header[aria-selected]:focus { 358 | -webkit-border-radius: 14px; 359 | -moz-border-radius: 14px; 360 | -ms-border-radius: 14px; 361 | -o-border-radius: 14px; 362 | border-radius: 14px; 363 | } 364 | .visually-hidden { 365 | position: absolute; 366 | overflow: hidden; 367 | clip: rect(0 0 0 0); 368 | height: 1px; 369 | width: 1px; 370 | margin: -1px; 371 | padding: 0; 372 | border: none; 373 | } -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test page 6 | 7 | 8 | 9 | 10 | 11 |

Test page

12 | 13 |

Tabset

14 | 15 |
16 |

Demo

17 |
18 | 19 |  panel content 

Here are a list of my favorite books: 20 |

    21 |
  1. Hitchhiker's Guide to the Galaxy
  2. 22 |
  3. Pride and Prejudice
  4. 23 |
  5. Green Eggs and Ham
  6. 24 |
25 |

26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/assertions.js: -------------------------------------------------------------------------------- 1 | QUnit.config.autostart = false; 2 | 3 | var verifyPanel = function (assert, el, opts) { 4 | var children = el.children, 5 | headerBox, 6 | contentBox, 7 | header, 8 | opts = opts || {}, 9 | expectedPanelRole = (opts.isInSet) ? "presentation" : "group"; 10 | 11 | assert.equal( 12 | el.getAttribute("role"), 13 | expectedPanelRole, 14 | "We expect role of a basic panel to be '" + expectedPanelRole + "'" 15 | ); 16 | 17 | assert.equal( 18 | children.length, 19 | 2, 20 | "there should be two child elements generated" 21 | ); 22 | 23 | headerBox = children[0]; 24 | contentBox = children[1]; 25 | 26 | assert.ok( 27 | headerBox.classList.contains("common-panel-header-box"), 28 | "the first child should be common-panel-header-box" 29 | ); 30 | 31 | assert.equal( 32 | headerBox.children.length, 33 | 1, 34 | "the headerbox should contain 1 child, this allows for :before and :after styling" 35 | ); 36 | 37 | header = headerBox.firstElementChild; 38 | 39 | assert.ok( 40 | header.hasAttribute("tabindex"), 41 | "the header should have a tab stop" 42 | ); 43 | 44 | assert.equal( 45 | headerBox.getAttribute("role"), 46 | "presentation", 47 | "the role of the headerbox should be presentation" 48 | ); 49 | 50 | assert.notOk( 51 | headerBox.hasAttribute("tabindex"), 52 | "the headerbox should not have a tab stop" 53 | ); 54 | 55 | 56 | assert.equal( 57 | headerBox.children.length, 58 | 1, 59 | "the headerbox should have exactly one child in the dom" 60 | ); 61 | 62 | assert.ok( 63 | headerBox.firstElementChild.classList.contains("common-panel-header"), 64 | "the headerBox should container a header element" 65 | ); 66 | 67 | assert.equal( 68 | headerBox.firstElementChild.children.length, 69 | 4, 70 | "the headerbox should have exactly four children in the dom" 71 | ); 72 | 73 | assert.ok( 74 | headerBox.firstElementChild.children[0].classList.contains("common-panel-icon"), 75 | "the headerbox contains a header whose first child is an icon slot" 76 | ); 77 | 78 | assert.equal( 79 | headerBox.firstElementChild.children[0].innerHTML.trim(), 80 | "", 81 | "the icon slot is purely a decorative hook like a pseudo-element, it should be empty" 82 | ); 83 | 84 | assert.ok( 85 | headerBox.firstElementChild.children[1].classList.contains("common-panel-title"), 86 | "the headerbox contains a header whose second child is the title slot" 87 | ); 88 | 89 | 90 | /* TODO: maybe we should pass a specific expectation in */ 91 | assert.ok( 92 | headerBox.firstElementChild.children[1].innerHTML.trim() != "", 93 | "the title slot should contain the title text, it shouldn't be empty" 94 | ); 95 | 96 | /* TODO: we should really check to be sure it isn't visible but is available 97 | to a screen reader */ 98 | assert.ok( 99 | headerBox.firstElementChild.children[2].classList.contains("visually-hidden"), 100 | "the headerbox contains a header whose first child is an icon slot" 101 | ); 102 | 103 | assert.equal( 104 | headerBox.firstElementChild.children[2].innerHTML.trim(), 105 | " panel ", 106 | "the panel title should announce that it is a 'panel' after reading the panel title" 107 | ); 108 | 109 | assert.ok( 110 | headerBox.firstElementChild.children[3].classList.contains("common-panel-remove"), 111 | "the headerbox contains a header whose fourth child is an a remove button" 112 | ); 113 | 114 | assert.equal( 115 | headerBox.firstElementChild.children[3].title, 116 | "Remove this panel", 117 | "the remove button should explain what it does in the title" 118 | ); 119 | 120 | /* TODO: this should be passed in */ 121 | assert[(opts.isRemovable) ? "notEqual" : "equal"]( 122 | headerBox.firstElementChild.children[3].style.display, 123 | "none", 124 | "the remove button should be hidden via display" 125 | ); 126 | 127 | assert.ok( 128 | contentBox.classList.contains("common-panel-content"), 129 | "the first child should be common-panel-content" 130 | ); 131 | 132 | assert[opts.isInSet ? "equal" : "notEqual"]( 133 | contentBox.getAttribute("role"), 134 | "tabpanel", 135 | "When part of a panelset, common-panel-content should have a role of 'tabpanel', otherwise none" 136 | ); 137 | 138 | assert[opts.isExpandable ? "equal" : "notEqual"]( 139 | contentBox.hasAttribute("aria-expanded"), 140 | "the aria-expanded attribute should be managed (or not) on the contentBox appropriately" 141 | ); 142 | 143 | assert.ok( 144 | contentBox.hasAttribute("tabindex"), 145 | "the contentbox should have a tabstop" 146 | ); 147 | 148 | 149 | /* TODO: we should really check to be sure it isn't visible but is available 150 | to a screen reader */ 151 | assert.ok( 152 | contentBox.firstElementChild.classList.contains("visually-hidden"), 153 | "the contentbox's first child should be visually hidden but announce itself as 'panel content'" 154 | ); 155 | 156 | /* TODO: add negative accessor/mutator DOM API verifications */ 157 | assert.ok(typeof el.headerElement, "Panel should have a headerElement accessor"); 158 | assert.ok(typeof el.contentElement, "Panel should have a contentElement accessor"); 159 | assert.ok(typeof el.contentElement, "Panel should have a tabProxyElement accessor"); 160 | assert.ok(typeof el.expansionState, "Panel should have a expansionState mutator"); 161 | assert.ok(typeof el.toggleExpansionState, "Panel should have a toggleExpansionState method"); 162 | 163 | /* TODO: add a test for what should happen if you call toggleExpansionState if the panel is not marked expandable? */ 164 | 165 | }, 166 | verifyPanelset = function (assert, panelset, opts) { 167 | var item, 168 | controlled, 169 | numPanels = opts.numPanels, 170 | selectedIndex = opts.selectedIndex || 0; 171 | 172 | assert.equal( 173 | panelset.getAttribute("role"), 174 | "tablist", 175 | "the role of a tabset should be tablist" 176 | ); 177 | 178 | assert.notOk( 179 | panelset.hasAttribute("tabindex"), 180 | "the panelset element should not have a tab stop" 181 | ); 182 | 183 | assert.ok( 184 | panelset.firstElementChild.classList.contains("common-panel-tabs"), 185 | "the first child of panelset should be common-panel-tabs" 186 | ); 187 | 188 | assert.notOk( 189 | panelset.firstElementChild.hasAttribute("tabindex"), 190 | "common-panel-tabs should not have a tab stop" 191 | ); 192 | 193 | assert.equal( 194 | panelset.firstElementChild.getAttribute("role"), 195 | "presentation", 196 | "the role of common-panel-tabs must be presentation" 197 | ); 198 | 199 | // TODO: configure #tabs assertions 200 | assert.equal( 201 | panelset.firstElementChild.children.length, 202 | numPanels, 203 | "common-elements-tabs should contain 1 child per panel" 204 | ); 205 | 206 | 207 | assert.equal( 208 | panelset.children.length, 209 | numPanels+1, 210 | "the panelset should have 1 common-panel per + 1 for common-panel-tabs" 211 | ); 212 | 213 | /* verify a proper tab has been created for each one... */ 214 | for (var i=0; i 2 | 3 | 4 | 5 | 6 | 7 | Panel and Panel Sets Prototypes 8 | 9 | 10 | 11 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | 48 |
49 |

Panel and Panel Sets Prototypes

50 |

This page contains descriptions, examples and instances of prototype implemention (prollyfill) of 51 | the Panels and Panel Sets Draft. It can be used 52 | for illustration and education, as well as serve as a host for tests for manual or automated 53 | checking of conformance for the prollyfill itself.

54 | 55 |
56 |

Panels

57 |
58 |

Basic Panel

59 |

This example demonstrates use of a simple, basic common-panel.

60 | <common-panel>
61 |   <common-panel-title>My favorite books</common-panel-title>
62 |   <p>Here are a list of my favorite books:
63 |     <ol>
64 |       <li>Hitchhiker's Guide to the Galaxy</li>
65 |       <li>Pride and Prejudice</li>
66 |       <li>Green Eggs and Ham</li>
67 |     </ol>
68 |   </p>
69 | </common-panel> 70 |
71 |
72 |

Demo

73 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel with 74 | a title "My favorite books" and paragraph with a list of three of one of the author's favorites. A user 75 | should be able to switch from the title to content by way of the TAB key.

76 | 77 | My favorite books 78 |

Here are a list of my favorite books: 79 |

    80 |
  1. Hitchhiker's Guide to the Galaxy
  2. 81 |
  3. Pride and Prejudice
  4. 82 |
  5. Green Eggs and Ham
  6. 83 |
84 |

85 |
86 |
87 |
88 | 89 |
90 |

Removable Panel

91 |

This example demonstrates use of a common-panel with the is-removable attribute set.

92 | <common-panel is-removable>
93 |   <common-panel-title>Reminder</common-panel-title>
94 |   <p>You should try using this with assistive technology and, if you are sighted, close your eyes.</p>
95 | </common-panel> 96 |
97 |
98 |

Demo

99 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel with 100 | a title "Reminder" and, as part of the title, "X" button which can be used to remove it. It should also contain content 101 | (the reminder) which is a one sentence paragraph about testing. Users should be able to TAB focus onto the title 102 | content, TAB again to the close button. Pressing spacebar should cause this panel to be removed from the 103 | document in its entirety. If not removed, TAB again should switch focus to the content. 104 |

105 | 106 | Reminder 107 |

You should try using this with assistive technology and, if you are sighted, close your eyes.

108 |
109 |
110 |
111 | 112 |
113 |

Expandable/Collapsable Panel

114 |

This example demonstrates the use of a common-panel with the a specified expansion-state attribute 115 | set to "opened".

116 | <common-panel expansion-state="opened">
117 |   <common-panel-title>Today's News</common-panel-title>
118 |   <p>In today's news, accessibility matters. Accessibility first design might actually help you realize some things that improve your design for visually oriented users as well, for example, this whole proposal!</p>
119 | </common-panel>
120 |
121 |

Demo

122 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel with the 123 | an expansion state of opened. The user should be able to TAB focus onto the title content and, when the panel is 124 | opened (as it is by default here) pressing the TAB should move focus to the content: a paragraph about acessibilty first design. If the user presses the spacebar on the title, as it is collapsable, this should cause the content to become hidden and removed from 125 | TAB navigation until it is re-opened.

126 | 127 | Today's News 128 |

In today's news, accessibility matters. Accessibility first design might actually help you realize some things that improve your 129 | design for visually oriented users as well, for example, this whole proposal!

130 |
131 |
132 |
133 |
134 | 135 |
136 |

Panel Sets

137 |
138 |

Simple Panel Set

139 |

This exaple demonstrates the use of common-panel-set surrounding a collection of simple, vanilla panels about 140 | themeatic groups of news stories. The panel set makes them all inherently "expandable" and manages their simple selection. 141 | As none claims to be expanded, the first one is expanded.

142 | <common-panel-set>
143 |     <common-panel>
144 |        <common-panel-title>Local</common-panel-title>
145 |        <p>This would contain content about local news stories.</p>
146 |     </common-panel>
147 |
148 |     <common-panel>
149 |        <common-panel-title>Entertainment</common-panel-title>
150 |        <p>This would contain content about entertainment news stories.</p>
151 |     </common-panel>
152 |
153 |     <common-panel>
154 |        <common-panel-title>Sports</common-panel-title>
155 |        <p>This would contain content about sports stories.</p>
156 |     </common-panel>
157 | </common-panel-set>
158 |
159 |

Demo

160 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 161 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 162 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 163 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set.

164 | 165 | 166 | Local 167 |

This would contain content about local news stories.

168 |
169 | 170 | 171 | Entertainment 172 |

This would contain content about entertainment news stories.

173 |
174 | 175 | 176 | Sports 177 |

This would contain content about sports stories.

178 |
179 |
180 |
181 |
182 | 183 |
184 |

Panel Set (explicit expansion)

185 |

This exaple demonstrates the use of common-panel-set surrounding a collection of panels about 186 | themeatic groups of news stories. As common-panel children claim their expansion state to be "opened", 187 | each is processed in document order and the common-panel-set closes others. This means, in a manner 188 | similar to the select element, the last one in document order to make the claim ultimately "wins".

189 | <common-panel-set>
190 |     <common-panel >
191 |        <common-panel-title>Local</common-panel-title>
192 |        <p>This would contain content about local news stories.</p>
193 |     </common-panel>
194 |
195 |     <common-panel expansion-state="opened">
196 |        <common-panel-title>Entertainment</common-panel-title>
197 |        <p>This would contain content about entertainment news stories.</p>
198 |     </common-panel>
199 |
200 |     <common-panel expansion-state="opened"
201 |        <common-panel-title>Sports</common-panel-title>
202 |        <p>This would contain content about sports stories.</p>
203 |     </common-panel>
204 | </common-panel-set>
205 |
206 |

Demo

207 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 208 | two of the common-panel children specify an expansion state of opened, thus, the last one should be. 209 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 210 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 211 | In all ways beyond which panel is initially expanded, this should be functionally identical to the previous example.

212 | 213 | 214 | Local 215 |

This would contain content about local news stories.

216 |
217 | 218 | 219 | Entertainment 220 |

This would contain content about entertainment news stories.

221 |
222 | 223 | 224 | Sports 225 |

This would contain content about sports stories.

226 |
227 |
228 |
229 |
230 | 231 |
232 |

Panel Set's preferred-display Attribute (tabset)

233 |

This example demonstrates the use of a preferred-display attribute set to tabset on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

234 | <common-panel-set preferred-display="tabset">
235 |     <common-panel>
236 |        <common-panel-title>Local</common-panel-title>
237 |        <p>This would contain content about local news stories.</p>
238 |     </common-panel>
239 |
240 |     <common-panel>
241 |        <common-panel-title>Entertainment</common-panel-title>
242 |        <p>This would contain content about entertainment news stories.</p>
243 |     </common-panel>
244 |
245 |     <common-panel>
246 |        <common-panel-title>Sports</common-panel-title>
247 |        <p>This would contain content about sports stories.</p>
248 |     </common-panel>
249 | </common-panel-set>
250 |
251 |

Demo

252 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 253 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 254 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 255 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 256 | It is functionally identical to the previous example, only the visual representation should change.

257 | 258 | 259 | Local 260 |

This would contain content about local news stories.

261 |
262 | 263 | 264 | Entertainment 265 |

This would contain content about entertainment news stories.

266 |
267 | 268 | 269 | Sports 270 |

This would contain content about sports stories.

271 |
272 |
273 |
274 |
275 | 276 | 277 |
278 |

Panel Set's preferred-display Attribute (carousel)

279 |

This example demonstrates the use of a preferred-display attribute set to carousel on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

280 | <common-panel-set preferred-display="carousel">
281 |     <common-panel>
282 |        <common-panel-title>Local</common-panel-title>
283 |        <p>This would contain content about local news stories.</p>
284 |     </common-panel>
285 |
286 |     <common-panel">
287 |        <common-panel-title>Entertainment</common-panel-title>
288 |        <p>This would contain content about entertainment news stories.</p>
289 |     </common-panel>
290 |
291 |     <common-panel>
292 |        <common-panel-title>Sports</common-panel-title>
293 |        <p>This would contain content about sports stories.</p>
294 |     </common-panel>
295 | </common-panel-set>
296 |
297 |

Demo

298 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 299 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 300 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 301 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 302 | It is functionally identical to the previous example, only the visual representation should change.

303 | 304 | 305 | Local 306 |

This would contain content about local news stories.

307 |
308 | 309 | 310 | Entertainment 311 |

This would contain content about entertainment news stories.

312 |
313 | 314 | 315 | Sports 316 |

This would contain content about sports stories.

317 |
318 |
319 |
320 |
321 | 322 |
323 |

Panel Set's preferred-display Attribute (accordion)

324 |

This example demonstrates the use of a preferred-display attribute set to accordion on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

325 | <common-panel-set preferred-display="accordion">
326 |     <common-panel>
327 |        <common-panel-title>Local</common-panel-title>
328 |        <p>This would contain content about local news stories.</p>
329 |     </common-panel>
330 |
331 |     <common-panel>
332 |        <common-panel-title>Entertainment</common-panel-title>
333 |        <p>This would contain content about entertainment news stories.</p>
334 |     </common-panel>
335 |
336 |     <common-panel>
337 |        <common-panel-title>Sports</common-panel-title>
338 |        <p>This would contain content about sports stories.</p>
339 |     </common-panel>
340 | </common-panel-set>
341 |
342 |

Demo

343 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 344 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 345 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 346 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 347 | It is functionally identical to the previous example, only the visual representation should change.

348 | 349 | 350 | Local 351 |

This would contain content about local news stories.

352 |
353 | 354 | 355 | Entertainment 356 |

This would contain content about entertainment news stories.

357 |
358 | 359 | 360 | Sports 361 |

This would contain content about sports stories.

362 |
363 |
364 |
365 |
366 | 367 |
368 |

Panel Set's preferred-display Attribute (accordion)

369 |

This example demonstrates the use of a preferred-display attribute set to accordion on a common-panel-set element with content identical to the previous example. Only the visual representation should change.

370 | <common-panel-set preferred-display="accordion">
371 |     <common-panel>
372 |        <common-panel-title>Local</common-panel-title>
373 |        <p>This would contain content about local news stories.</p>
374 |     </common-panel>
375 |
376 |     <common-panel>
377 |        <common-panel-title>Entertainment</common-panel-title>
378 |        <p>This would contain content about entertainment news stories.</p>
379 |     </common-panel>
380 |
381 |     <common-panel>
382 |        <common-panel-title>Sports</common-panel-title>
383 |        <p>This would contain content about sports stories.</p>
384 |     </common-panel>
385 | </common-panel-set>
386 |
387 |

Demo

388 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 389 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 390 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 391 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 392 | It is functionally identical to the previous example, only the visual representation should change.

393 | 394 | 395 | Local 396 |

This would contain content about local news stories.

397 |
398 | 399 | 400 | Entertainment 401 |

This would contain content about entertainment news stories.

402 |
403 | 404 | 405 | Sports 406 |

This would contain content about sports stories.

407 |
408 |
409 |
410 |
411 | 412 | 413 |
414 |

Controlling preferred-display via Media Queries

415 |

This example demonstrates the use of media queries to control preferred-display attribute to make it tabset on wide screen, but accordion on all others. The common-panel-set element with content identical to the previous example. Only the visual representation should change.

416 | <!-- demo uses script type="text/MediaQueryMappings" lightweight polyfill -->
417 | <style>
418 |     @media all {
419 |         #foo {
420 |             --preferred-display: accordion;
421 |         }
422 |     }
423 |
424 |     @media screen and (min-width: 767px) {
425 |         #foo {
426 |             --preferred-display: tabset;
427 |         }
428 |      }
429 | </style>
430 | 431 | <common-panel-set id="foo">
432 |     <common-panel>
433 |        <common-panel-title>Local</common-panel-title>
434 |        <p>This would contain content about local news stories.</p>
435 |     </common-panel>
436 |
437 |     <common-panel>
438 |        <common-panel-title>Entertainment</common-panel-title>
439 |        <p>This would contain content about entertainment news stories.</p>
440 |     </common-panel>
441 |
442 |     <common-panel>
443 |        <common-panel-title>Sports</common-panel-title>
444 |        <p>This would contain content about sports stories.</p>
445 |     </common-panel>
446 | </common-panel-set>
447 |
448 |

Demo

449 |

Below is a running demo of the above code snippet using the prototype. It contains a common-panel-set in which 450 | none of the common-panel children specify an expansion state of opened, thus, the first one should be. 451 | The arrow keys should allow the user to navigate panels which open automatically, adding their content's ability to receive focus. 452 | TAB should move the focus from title to open content and then to the next focusable thing after the common-panel-set. 453 | Note that as its representation changes, neither the panel selection, nor the tab-focus should be lost. 454 | It is functionally identical to the previous example, only the visual representation should change.

455 | 456 | 457 | Local 458 |

This would contain content about local news stories.

459 |
460 | 461 | 462 | Entertainment 463 |

This would contain content about entertainment news stories.

464 |
465 | 466 | 467 | Sports 468 |

This would contain content about sports stories.

469 |
470 |
471 |
472 |
473 |
474 | 475 |
476 | 477 | 478 | 479 | --------------------------------------------------------------------------------