OLD | NEW |
---|---|
(Empty) | |
1 --- | |
2 layout: default | |
3 title: "Polymer Elements" | |
4 description: "Use Polymer elements to encapsulate and share custom DOM elements. " | |
5 has-permalinks: true | |
6 tutorial: | |
7 id: polymer-intro | |
8 next: fetchdata | |
9 next-title: "Fetch Data Dynamically" | |
10 prev: custom-elements | |
11 prev-title: "Define a Custom DOM Tag" | |
ericbidelman
2013/09/24 18:18:42
"Define a Custom HTML Element"
mem
2013/10/01 16:05:31
Done.
| |
12 --- | |
13 | |
14 {% capture whats_the_point %} | |
15 | |
16 * Polymer.dart is the next evolution of Web UI. | |
17 * Everything in Polymer.dart is an element. | |
18 * Polymer elements provide semantically meaningful encapsulation. | |
19 * Use Polymer.dart to build custom tags. | |
20 * Bind Dart data to HTML elements. | |
21 * Declaratively bind event handlers to elements. | |
22 | |
23 {% endcapture %} | |
24 | |
25 {% capture sample_links %} | |
26 | |
27 <p> | |
28 Get the source code for the samples featured in this target:</p> | |
29 | |
30 <ul> | |
31 <li> | |
32 <a href="https://github.com/dart-lang/dart-tutorials-samples/tree/master/web /target06-polymer/stopwatch" | |
33 target="_blank">stopwatch</a> | |
34 </li> | |
35 </ul> | |
36 | |
37 {% endcapture %} | |
38 | |
39 {% capture content %} | |
40 | |
41 <div class="tute-target-title"> | |
42 <h1>{{page.title}}</h1> | |
43 <h3>Create new HTML tags with Polymer elements</h3> | |
44 </div> | |
45 | |
46 Polymer elements are one feature of the | |
47 <a href="http://www.polymer-project.org/">Polymer</a> project, | |
48 whose primary aim is to help manage the complexity | |
sethladd
2013/09/20 23:18:36
This sounds like we're speaking for polymer projec
Jennifer Messerly
2013/09/24 02:36:39
borrow some language from "Guiding principals" on
mem
2013/10/01 16:05:31
I got this language from their website.
Changed it
| |
49 of building web applications. | |
50 <a href="http://www.dartlang.org/polymer-dart/">Polymer.dart</a> | |
51 is the Dart implementation of Polymer. | |
52 | |
53 A Polymer element is a customized element you can define yourself, | |
54 encapsulating appearance and/or behavior within semantically meaningful DOM tags . | |
ericbidelman
2013/09/24 18:18:42
DOM tags -> HTML elements
mem
2013/10/01 16:05:31
Done.
| |
55 | |
56 * [An example](#an-example) | |
57 * [Installing Polymer.dart](#getting-polymer-dart) | |
58 * [Including Polymer.dart in your application](#bootstrap) | |
59 * [Instantiating a Polymer element](#instantiating) | |
60 * [Defining a Polymer element](#define-element) | |
61 * [Providing a template for the Polymer element](#providing-a-template) | |
62 * [Providing a script for the Polymer element](#providing-a-script) | |
63 * [Overiding life-cycle methods](#life-cycle-methods) | |
64 * [Embedding live data on the page](#one-way-data-binding) | |
65 * [Setting up event handlers declaratively](#event-handlers) | |
66 * [Querying the shadow DOM](#in-the-shadows) | |
67 * [Styling a Polymer element](#scoped-css) | |
68 * [Other resources](#other-resources) | |
69 | |
70 ##An example | |
71 | |
72 In the example running below, | |
ericbidelman
2013/09/24 18:18:42
is a custom element, implemented using Polymer.
mem
2013/10/01 16:05:31
Done.
| |
73 the area within the black rectangle is implemented by a Polymer element, | |
sethladd
2013/09/20 23:18:36
there are two black rectangles
mem
2013/10/01 16:05:31
Done.
| |
74 | |
75 <strong>Try it!</strong> | |
Jennifer Messerly
2013/09/24 02:36:39
FYI -- the example didn't work for me, but it work
mem
2013/10/01 16:05:31
The link here is actually the Web UI version of th
| |
76 Start and stop the stopwatch. | |
77 Reset the stopwatch to 00:00 using the **Reset** button. | |
78 | |
79 <iframe class="running-app-frame" | |
80 style="height:175px;width:202px;" | |
81 src="http://dart-lang.github.com/dart-tutorials-samples/web/target06/sto pwatch/web/out/stopwatch.html"> | |
82 </iframe> | |
83 | |
84 To place this Polymer element on an HTML page, you write: | |
sethladd
2013/09/20 23:18:36
you also need to import it
mem
2013/10/01 16:05:31
Done.
| |
85 | |
86 {% prettify html %} | |
87 <tute-stopwatch></tute-stopwatch> | |
88 {% endprettify %} | |
89 | |
90 The counting text, the three buttons along with their actions, | |
91 and the style are all contained within the Polymer element. | |
92 The definition of the Polymer element encapsulates and | |
93 hides the implementation details, | |
94 which as the user of the element, you care nothing about. | |
95 | |
96 When you use tools to inspect the element, | |
97 you see just the Polymer element's begin and end tag. | |
98 The contents of the Polymer element are hidden in the *shadow DOM*. | |
sethladd
2013/09/20 23:18:36
define shadow dom here, or omit because you don't
mem
2013/10/01 16:05:31
Done.
| |
99 | |
100  | |
101 | |
102 With Polymer elements, developers can easily create elements | |
103 with semantically meaningful tags that are easy to share, | |
104 re-use, and read. | |
105 | |
106 ###Overview of the example files | |
107 | |
108 Three primary source files implement the stopwatch example: | |
109 | |
110 <dl> | |
111 <dt> | |
112 <a href="https://github.com/dart-lang/dart-tutorials-samples/tree/master/web /target06-polymer/stopwatch/web/index.html" target="_blank">index.html</a> | |
113 </dt> | |
114 <dd> | |
115 The primary HTML file for the app. | |
116 Includes the Polymer bootstrap script and instantiates the Polymer element. | |
117 </dd> | |
118 <dt> | |
119 <a href="https://github.com/dart-lang/dart-tutorials-samples/tree/master/web /target06-polymer/stopwatch/web/tute_stopwatch.html" target="_blank">tute_stopwa tch.html</a> | |
120 </dt> | |
121 <dd> | |
122 The HTML code that defines the Polymer element. | |
123 </dd> | |
124 <dt> | |
125 <a href="https://github.com/dart-lang/dart-tutorials-samples/tree/master/web /target06-polymer/stopwatch/web/tute_stopwatch.dart" target="_blank">tute_stopwa tch.dart</a> | |
126 </dt> | |
127 <dd> | |
128 The Dart class that implements the Polymer element. | |
129 </dd> | |
130 </dl> | |
131 | |
132 The following diagram shows the structure of the example | |
133 app and its use of Polymer elements. | |
ericbidelman
2013/09/24 18:18:42
There's a lot mentions of "Polymer elements", but
mem
2013/10/01 16:05:31
Changed Polymer elements -> custom elements everyw
| |
134 | |
135  | |
136 | |
137 ##Installing Polymer.dart {#getting-polymer-dart} | |
138 | |
139 To use the features provided by Polymer.dart, | |
140 you need to install the polymer package. | |
141 If you are unfamiliar with installing packages, | |
142 refer to | |
143 <a href="/docs/tutorials/shared-pkgs/">Install Shared Packages</a>, | |
144 which describes the process in detail. | |
145 | |
146 In brief, to install the polymer package: | |
147 | |
148 * In the application's `pubspec.yaml` file, | |
149 add the package to the list of dependencies | |
150 by adding the package name, `polymer`, to the list. | |
151 YAML is whitespace-sensitive | |
152 so take care to indent the package name as shown: | |
153 | |
154  | |
155 | |
156 * If you are using Dart Editor, | |
157 when you save pubspec.yaml | |
158 the editor automatically runs `pub install`. | |
159 If you are using command line tools, | |
160 you can run it with the command `pub install`. | |
161 The installation process | |
162 recursively installs the polymer.dart package | |
163 and all the packages that it depends on. | |
164 | |
165 ##Including Polymer.dart in your application {#bootstrap} | |
166 | |
167 To use Polymer.dart features, such as Polymer elements, | |
168 you need to include Polymer in both | |
169 the HTML side and in the Dart side of your app. | |
170 | |
171 * In the primary HTML file for your app, | |
172 you must include `packages/polymer/boot.js` | |
173 in the <head> section. | |
174 When using this script, | |
175 you do not need to include a top-level `main()` function, | |
176 although you may. | |
177 **Note:** Use this script in lieu of `packages/browser/dart.js`. | |
sethladd
2013/09/20 23:18:36
concerned that in lieu will be difficult for non-e
mem
2013/10/01 16:05:31
Done.
| |
178 | |
179  | |
180 | |
181 * Each Dart file that uses Polymer features | |
182 needs to import the Polymer library: | |
183 | |
184  | |
185 | |
186 ##Instantiating a Polymer element {#instantiating} | |
187 | |
188 To create an instance of the Polymer element, | |
189 use the name of the Polymer element just as you would any normal HTML tag. | |
190 In this example, the tag name is `tute-stopwatch`. | |
191 | |
192  | |
193 | |
194 Using best practices, | |
195 the Polymer element definition is in a separate file. | |
196 Use `link` to import the HTML definition file as shown. | |
ericbidelman
2013/09/24 18:18:42
`link[rel="import"]` to be clear
mem
2013/10/01 16:05:31
Done.
| |
197 | |
198 ##Defining a Polymer element {#define-element} | |
199 | |
200 The definition for the tute-stopwatch element is | |
ericbidelman
2013/09/24 18:18:42
<tute-stopwatch>
mem
2013/10/01 16:05:31
Done.
| |
201 in `tute-stopwatch.html`. | |
sethladd
2013/09/20 23:18:36
use underscore instead of dash
mem
2013/10/01 16:05:31
Done.
| |
202 A Polymer element definition should be in its own | |
203 source file so that it can be included by other files. | |
204 An HTML file that contains a Polymer element definition | |
205 does not need <html>, <head>, or <body> tags. | |
206 | |
207 To define a Polymer element, | |
208 use the <polymer-element> tag and provide a name. | |
209 | |
210 {% prettify html %} | |
211 <polymer-element name="tute-stopwatch"> | |
212 ... | |
213 </polymer-element> | |
214 {% endprettify %} | |
215 | |
216 A Polymer element name must have at least one hyphen (`-`). | |
217 We advise using an identifiable prefix to | |
218 avoid naming conflicts with elements shared by others | |
219 and to help identify the project from which the element originates. | |
220 For example, for tutorial polymer elements, we use the prefix `tute`. | |
221 | |
222 Within the <polymer-element> tag, | |
223 you can provide a template (appearance) and a script (behavior). | |
224 UI widgets, like our stopwatch example, | |
225 typically have both a template and a script. | |
226 But neither is required. | |
227 A polymer element with a script and no template is purely functional, | |
228 for example a [xx]. | |
ericbidelman
2013/09/24 18:18:42
are these placeholders?
mem
2013/10/01 16:05:31
Yes they are. Was hoping for a suggestion!
On 201
| |
229 A polymer element with a template and no script is purely visual, | |
230 for example a [xx]. | |
231 | |
232 {% prettify html %} | |
233 <polymer-element name="tute-stopwatch"> | |
234 <template> | |
235 ... | |
236 </template> | |
237 <script type="application/dart" src="tute_stopwatch.dart"> | |
238 </script> | |
239 </polymer-element> | |
240 {% endprettify %} | |
241 | |
242 <dl> | |
243 <dt> <template> </dt> | |
244 <dd> | |
245 Describes the element's structure—its user interface. | |
246 The template comprises any valid HTML code within the <template> tag. | |
ericbidelman
2013/09/24 18:18:42
It's important to note here that Polymer takes the
mem
2013/10/01 16:05:31
Reworded. Avoided using the term shadow root here.
| |
247 The template is rendered when a Polymer element is instantiated. | |
248 The template can also include scoped CSS styles within a <style> tag. | |
ericbidelman
2013/09/24 18:18:42
FYI, the styles are scoped by default.
mem
2013/10/01 16:05:31
Done.
| |
249 </dd> | |
250 | |
251 <dt> <script> </dt> | |
252 <dd markdown="1"> Specifies a Dart script. | |
253 For Polymer elements, the Dart script is a Dart class | |
ericbidelman
2013/09/24 18:18:42
Can Polymer.dart do inline <script>?
mem
2013/10/01 16:05:31
Yes. But our tools don't give good info when editi
| |
254 that implements the behavior of the element. | |
255 The class typically overrides some | |
256 life-cycle methods and provides event handlers | |
257 that join the UI with its programmatic behavior. | |
258 In this example, the script is in the `tute_stopwatch.dart` file. | |
259 </dd> | |
260 </dl> | |
261 | |
262 ##Providing a template for the Polymer element {#providing-a-template} | |
263 | |
264 Here's the template code for the tute-stopwatch element: | |
265 | |
266  | |
267 | |
268 The tute-stopwatch template uses a <style> tag, which is optional. | |
269 These styles are scoped; they affect only | |
270 the appearance of the Polymer element and the elements it contains. | |
271 More about scoped CSS in [Styling a Polymer element](#scoped-css). | |
ericbidelman
2013/09/24 18:18:42
Suggset you also link to this article: http://www.
mem
2013/10/01 16:05:31
Done.
| |
272 | |
273 The rest of the code within the template tag | |
ericbidelman
2013/09/24 18:18:42
<template>
mem
2013/10/01 16:05:31
Done.
| |
274 is normal HTML with two exceptions: | |
275 | |
276 * {% raw %}`{{counter}}`{% endraw %}--a Polymer syntax for | |
ericbidelman
2013/09/24 18:18:42
"a mustache for data binding"
mem
2013/10/01 16:05:31
Included reference to mustaches, but worded it dif
| |
277 [embedding live data on the page](#one-way-data-binding). | |
278 | |
279 * `on-click`--a Polymer element attribute that lets you | |
ericbidelman
2013/09/24 18:18:42
"a Polymer element attribute" -> a Polymer `on-*`d
mem
2013/10/01 16:05:31
Done.
| |
280 [declaratively attach a mouse click handler](#event-handlers) | |
281 to UI elements, such as buttons. | |
282 Polymer has other attributes for other event types, such as `on-change`. | |
283 | |
284 Let's take a look at the structure of the Dart code | |
285 before we get into the details of data binding, event handlers, | |
286 and scoped CSS. | |
287 | |
288 ##Providing a script for the Polymer element {#providing-a-script} | |
289 | |
290 When you define a Polymer element in HTML, | |
291 you give it a name using the `name` attribute. | |
292 On the Dart side, a class implements the behavior of the Polymer element. | |
293 You associate the Dart class with the Polymer element using the `@CustomTag` | |
294 annotation and the name of the Polymer element. | |
295 | |
296  | |
297 | |
298 This diagram gives an overview of the TuteStopwatch class: | |
299 | |
300  | |
301 | |
302 Any Dart class that backs a Polymer element must subclass PolymerElement. | |
303 For now, if you want to use data binding, | |
304 the class must also use ObservableMixin. | |
Jennifer Messerly
2013/09/24 02:36:39
this will be fixed soon! this week or next. I've a
| |
305 | |
306 [xx: more about PolymerElement] | |
307 | |
308 [xx: more about ObservableMixin] | |
309 | |
310 The class may override one of several [life-cycle methods](#life-cycle-methods) | |
311 such as the inserted() method you see overridden here. | |
312 | |
313 The `start()` method is an event handler for the **Start** button. | |
314 The event handler is declaratively connected to the button. | |
315 Refer to [Setting up event handlers declaratively](#event-handlers) to see how. | |
316 | |
317 ##Overriding life-cycle methods {#life-cycle-methods} | |
318 | |
319 The stopwatch example overrides the inserted() method | |
320 to initialize the app. | |
321 The app needs a reference to each of the three buttons | |
322 so that it can enable and disable the buttons | |
323 as appropriate for the state of the app. | |
324 You'll notice that it gets the buttons by | |
325 [querying its shadow DOM](#in-the-shadows). | |
ericbidelman
2013/09/24 18:18:42
Do you guys have http://www.polymer-project.org/po
Jennifer Messerly
2013/09/24 18:30:06
not yet. This change is landing soon: https://code
| |
326 | |
327 {% prettify dart %} | |
328 void inserted() { | |
329 super.inserted(); | |
330 startButton = getShadowRoot("tute-stopwatch").query('#startbutton'); | |
331 stopButton = getShadowRoot("tute-stopwatch").query('#stopbutton'); | |
332 resetButton = getShadowRoot("tute-stopwatch").query('#resetbutton'); | |
333 | |
334 stopButton.disabled = true; | |
335 resetButton.disabled = true; | |
336 } | |
337 {% endprettify %} | |
338 | |
339 The inserted() method is one of four Polymer element life-cycle methods: | |
ericbidelman
2013/09/24 18:18:42
FYI: the lifecycle methods have been renamed. See
Jennifer Messerly
2013/09/24 18:30:06
yup. We'll have to fix this once a few of our code
| |
340 | |
341 * created(): called when an instance of a Polymer element is created. | |
342 | |
343 * inserted(): called when an instance of a Polymer element is | |
344 inserted into the DOM. | |
345 | |
346 * removed(): called when an instance of a Polymer element is | |
347 removed from the DOM. | |
348 | |
349 * attributeChanged(): called when an attribute, such as `class`, | |
350 of an instance of the Polymer element is added, changed, or removed. | |
351 | |
352 You can override any of these life-cycle methods. | |
353 The overriding method | |
354 *must* call the super class methods first. | |
355 | |
356 ##Embedding live data on the page {#one-way-data-binding} | |
ericbidelman
2013/09/24 18:18:42
I think the title is misleading. "Using two-way da
mem
2013/10/01 16:05:31
Done.
| |
357 | |
358 In HTML definition of a Polymer element, | |
359 use double curly brackets to embed Dart data into your webpage. | |
360 In Dart use the `@observable` annotation in the Dart code | |
361 to mark the embedded data. | |
362 Here, the data is a string called `counter`. | |
363 | |
364  | |
365 | |
366 The stopwatch element uses a periodic Timer to fire an event every second. | |
Jennifer Messerly
2013/09/24 02:36:39
link to API docs for Timer?
mem
2013/10/01 16:05:31
Done.
| |
367 When the Timer fires, it calls the `updateTImer` callback function, | |
sethladd
2013/09/20 23:18:36
typo
mem
2013/10/01 16:05:31
Done.
| |
368 which modifies the `counter` string. | |
369 Polymer takes care of update the bound area on the HTML page. | |
370 | |
371 This type of binding is called _one-way data binding_ | |
372 because the data can change only on the Dart side. | |
373 Polymer also supports two-way data binding | |
374 in which the data can be changed on the HTML side, | |
375 for example with an input element, | |
376 and it changes the value in the Dart code. | |
377 | |
378 You can use expressions within the double curly brackets. | |
379 <a href="http://pub.dartlang.org/packages/polymer_expressions">Polymer expressio ns</a> | |
380 provide the default syntax. Examples of allowable expressions include: | |
381 | |
382 * {% raw %} `{{myObject.aProperty}}` {% endraw %}--property access | |
383 * {% raw %} `{{!empty}}` {% endraw %}--operators, like the logical not operator | |
384 * {% raw %} `{{myList[3]}}` {% endraw %}--list indexing | |
385 * {% raw %} `{{myFunction()}}` {% endraw %}--function calls | |
sethladd
2013/09/20 23:18:36
let's not mention function calls here. not a great
Jennifer Messerly
2013/09/24 02:36:39
mention filters? that's a good time to include fun
mem
2013/10/01 16:05:31
changed the name to make it look like a filter. Do
| |
386 | |
387 ##Setting up event handlers declaratively {#event-handlers} | |
388 | |
389 This example has three buttons, each | |
390 with an event handler that is written in Dart, | |
391 but attached to the button declaratively from HTML. | |
392 | |
393  | |
394 | |
395 In HTML, use the `on-click` attribute | |
396 to attach a mouse click handler to an HTML element. | |
397 The value of the attribute is the name of a method | |
398 in the class that implements the Polymer element. | |
399 When the user clicks the button the named method is called | |
400 with three parameters: | |
401 | |
402 * the | |
403 <a href="https://api.dartlang.org/dart_html/Event.html" target="_blank">Event</a > | |
404 object contains information about the event, | |
405 such as its type, and when it occurred. | |
406 | |
407 * detail [xx] has some vague use I'm uncertain of. | |
sethladd
2013/09/20 23:18:36
an object can be attached to the event. it's the "
mem
2013/10/01 16:05:31
Done.
| |
408 | |
409 * the <a href="https://api.dartlang.org/dart_html/Node.html" target="_blank">Nod e</a>, | |
410 which fired the event—the **Start** button in this case. | |
411 | |
412 You can attach event handlers for other kinds of events. | |
413 For example, you can use `on-change` to handle events for input text elements | |
414 when the text changes. | |
415 | |
416 ##Querying the Shadow DOM {#in-the-shadows} | |
sethladd
2013/09/20 23:18:36
I'd like to delay talking about or mentioning shad
mem
2013/10/01 16:05:31
I've got all the shadow dom stuff isolated here in
| |
417 | |
418 Hey! Remember we mentioned the shadow DOM earlier. | |
419 Now we get to bring it out of the shadows. | |
420 | |
421 The Shadow DOM is key to encapsulation. | |
422 The DOM tree for a Polymer element is in the shadows, | |
423 hidden from outside of the Polymer element | |
424 (unless the Polymer element chooses to reveal them). | |
ericbidelman
2013/09/24 18:18:42
Not sure what you mean here. Shadow roots are disc
mem
2013/10/01 16:05:31
Done.
| |
425 | |
426  | |
427 | |
428 You can programmatically get items from the shadow DOM | |
ericbidelman
2013/09/24 18:18:42
Be consistent with "Shadow DOM" capitalization.
mem
2013/10/01 16:05:31
Done.
| |
429 by querying a Polymer element's shadow root. | |
430 | |
431 {% prettify dart %} | |
432 startButton = getShadowRoot("tute-stopwatch").query('#startbutton'); | |
433 stopButton = getShadowRoot("tute-stopwatch").query('#stopbutton'); | |
434 resetButton = getShadowRoot("tute-stopwatch").query('#resetbutton'); | |
435 {% endprettify %} | |
436 | |
437 Call `getShadowRoot` with the name of the Polymer element. | |
438 The getShadowRoot function returns the shadow root element | |
ericbidelman
2013/09/24 18:18:42
`getShadowRoot`
mem
2013/10/01 16:05:31
Done.
| |
439 for 'this' instance of the element named. | |
ericbidelman
2013/09/24 18:18:42
`this`
mem
2013/10/01 16:05:31
Done.
| |
440 Then use `query` with a CSS selector to get the element(s) of interest. | |
441 | |
442 Note that this code uses query to get each button by id. | |
443 By querying the shadow root object rather than the DOM, | |
444 you are guaranteed to get the objects from within the Polymer element, | |
445 not from anywhere else on the page. | |
446 | |
447 ##Styling a Polymer element {#scoped-css} | |
448 | |
449 You can optionally include CSS styles for your Polymer element | |
450 which apply only to the contents of the Polymer element. | |
ericbidelman
2013/09/24 18:18:42
You can also style the element directly using `@ho
mem
2013/10/01 16:05:31
I couldn't get this to work.
On 2013/09/24 18:18:
mem
2013/10/03 17:01:54
Got it done now.
Adjusted the example and text.
On
| |
451 | |
452  | |
Jennifer Messerly
2013/09/24 02:36:39
"Scoped CSS styling"?
mem
2013/10/01 16:05:31
Done.
| |
453 | |
454 The CSS here provide a style for the ID `stopwatch_container`, | |
455 which identifies the primary <div> element within the stopwatch template. | |
456 The styles within the template apply only to the template. | |
457 So any CSS selectors within the template need only be unique within the template . | |
458 You don't need to worry about naming conflicts on the page. | |
459 | |
460 ##Other resources | |
461 | |
462 Use these other resources to learn more about Polymer.dart: | |
463 | |
464 * We converted all tutorial examples that previously used Web UI | |
465 to use Polymer instead. | |
466 Check out the | |
467 <a href="https://github.com/dart-lang/dart-tutorials-samples/tree/master/web">gi thub repo</a> | |
468 to find the source code for all of the Polymer examples. | |
469 | |
470 * Learn more about | |
471 <a href="http://www.dartlang.org/polymer-dart/">Polymer.dart</a>—the | |
472 Dart port of the Polymer project. | |
473 | |
474 * Check out the Polymer project website at | |
475 <a href="http://www.polymer-project.org/">polymer-project.org</a>. | |
476 | |
477 * The next target is going to talk about other nifty polymer.dart features like | |
478 two-way data binding, xtag, and the <content> tag and shadow DOM. | |
479 Aren't you excited? | |
480 | |
481 Wahoo! | |
482 | |
483 Go write some code. | |
484 | |
485 {% endcapture %} | |
486 | |
487 {% include tutorial.html %} | |
OLD | NEW |