OB电竞官方软件下载
My typical workflow using a desktop word processor goes something like this:
- Select some text I want to copy to another part of the document.
- Note that the application has selected slightly more or less than I told it to.
- Try again.
- Give up and resolve to add the missing part (or remove the extra part) of my intended selection later.
- Copy and paste the selection.
- Note that the formatting of the pasted text is somehow different from the original.
- Try to find the styling preset that matches the original text.
- Try to apply the preset.
- Give up and apply the font family and size manually.
- Note that there is too much white space above the pasted text, and press “Backspace” to close the gap.
- Note that the text in question has elevated itself several lines at once, joined the heading text above it and adopted its styling.
- Ponder my mortality.
When writing technical web documentation (read: pattern libraries ), word processors are not just disobedient, but inappropriate. Ideally, I want a mode of writing that allows me to include the components I’m documenting inline, and this isn’t possible unless the documentation itself is made of HTML, CSS, and JavaScript. In this article, I’ll be sharing a method for easily including code demos in Markdown, with the help of shortcodes and shadow DOM encapsulation.

CSS And Markdown
Say what you will about CSS, but it’s certainly a more consistent and reliable typesetting tool than any WYSIWYG editor or word processor on the market. Why? Because there’s no high-level black-box algorithm that tries to second-guess what styles you really intended to go where. Instead, it’s very explicit: You define which elements take which styles in which circumstances , and it honors those rules.
The only trouble with CSS is that it requires you to write its counterpart, HTML. Even great lovers of HTML would likely concede that writing it manually is on the arduous side when you just want to produce prose content. This is where Markdown comes in. With its terse syntax and reduced feature set, it offers a mode of writing that is easy to learn but can still — once converted into HTML programmatically — harness CSS’ powerful and predictable typesetting features. There’s a reason why it has become the de facto format for static website generators and modern blogging platforms such as Ghost.
Where more complex, bespoke markup is required, most Markdown parsers will accept raw HTML in the input. However, the more one relies on complex markup, the less accessible one’s authoring system is to those who are less technical, or those short on time and patience. This is where shortcodes come in.
Shortcodes In Hugo
Hugo is a static site generator written in Go — a multi-purpose, compiled language developed at Google. Due to concurrency (and, no doubt, other low-level language features I don’t fully understand), Go makes Hugo a lightening-fast generator of static web content. This is one of the many reasons why Hugo has been chosen for the new version of Smashing Magazine.
Performance aside, it works in a similar fashion to the Ruby and Node.js -based generators with which you may already be familiar: Markdown plus meta data (YAML or TOML) processed via templates. Sara Soueidan has written an excellent primer on Hugo’s core functionality.
For me, Hugo’s killer feature is its implementation of shortcodes . Those coming from WordPress may already be familiar with the concept: a shortened syntax primarily used for including the complex embed codes of third-party services. For instance, WordPress includes a Vimeo shortcode that takes just the ID of the Vimeo video in question.
[vimeo 44633289]
The brackets signify that their content should be processed as a shortcode and expanded into the full HTML embed markup when the content is parsed.
Making use of Go template functions, Hugo provides an extremely simple API for creating custom shortcodes. For example, I have created a simple Codepen shortcode to include among my Markdown content:
Some Markdown content before the shortcode. Aliquam sodales rhoncus dui, sed congue velit semper ut. Class aptent taciti sociosqu ad litora torquent. {{
}} Some Markdown content after the shortcode. Nulla vel magna sit amet dui lobortis commodo vitae vel nulla sit amet ante hendrerit tempus.
Hugo automatically looks for a template named
codePen.html
in the
shortcodes
subfolder to parse the shortcode during compilation. My implementation looks like this:
{{ if .Site.Params.codePenUser }}
{{ else }}
Site error:
The
codePenUser
param has not been set in
config.toml
{{ end }}
To get a better idea of how the Go template package works, you’ll want to consult Hugo’s “ Go Template Primer .” In the meantime, just note the following:
- It’s pretty fugly but powerful nonetheless.
-
The
{{ .Get 0 }}
part is for retrieving the first (and, in this case, only) argument supplied — the Codepen ID. Hugo also supports named arguments, which are supplied like HTML attributes. -
The
.
syntax refers to the current context. So,.Get 0
means “Get the first argument supplied for the current shortcode.”
In any case, I think shortcodes are the best thing since shortbread, and Hugo’s implementation for writing custom shortcodes is impressive. I should note from my research that it’s possible to use Jekyll includes to similar effect, but I find them less flexible and powerful.
Code Demos Without Third Parties
I have a lot of time for Codepen (and the other code playgrounds that are available), but there are inherent issues with including such content in a pattern library:
- It uses an API so cannot be easily or efficiently made to work offline.
- It doesn’t just represent the pattern or component; it is its own complex interface wrapped in its own branding. This creates unnecessary noise and distraction when the focus should be on the component.
For some time, I tried to embed component demos using my own iframes. I would point the iframe to a local file containing the demo as its own web page. By using iframes, I was able to encapsulate style and behavior without relying on a third party.
Unfortunately, iframes are rather unwieldy and difficult to resize dynamically. In terms of authoring complexity, it also entails maintaining separate files and having to link to them. I’d prefer to write my components in place, including just the code needed to make them work. I want to be able to write demos as I write their documentation.
The
demo
Shortcode
Fortunately, Hugo allows you to create shortcodes that include content between opening and closing shortcode tags. The content is available in the shortcode file using
{{ .Inner }}
. So, suppose I were to use a
demo
shortcode like this:
{{
}} This is the content! {{
}}
“This is the content!” would be available as
{{ .Inner }}
in the
demo.html
template that parses it. This is a good starting point for supporting inline code demos, but I need to address encapsulation.
Style Encapsulation
When it comes to encapsulating styles, there are three things to worry about:
- styles being inherited by the component from the parent page,
- the parent page inheriting styles from the component,
- styles being shared unintentionally between components.
One solution is to carefully manage CSS selectors so that there’s no overlap between components and between components and the page. This would mean using esoteric selectors per component, and it is not something I would be interested in having to consider when I could be writing terse, readable code. One of the advantages of iframes is that styles are encapsulated by default, so I could write
button { background: blue }
and be confident it would only apply inside the iframe.
A less intensive way to prevent components from inheriting styles from the page is to use the
all
property with the
initial
value on an elected parent element. I can set this element in the
demo.html
file:
{{ .Inner }}
Then, I need to apply
all: initial
to instances of this element, which propagates to children of each instance.
.demo { all: initial }
The behavior of
Note:
Using
Nonetheless, this only deals with styles coming from the parent into components. To prevent styles written for components from affecting other parts of the page, we'll need to use
shadow DOM
to create an encapsulated subtree.
Imagine I want to document a styled
The trick is to take the
I'd also like to include JavaScript behavior in my components. At first, I thought this would be easy; unfortunately, JavaScript inserted via
Now, I'm able to include an inline demo of, say, a working toggle button:
Note:
I have written in depth about
toggle buttons and accessibility
for Inclusive Components.
JavaScript is, to my surprise,
not encapsulated automatically
like CSS is in shadow DOM. That is, if there was another
What I need is an equivalent to
I didn't want to have to write this expression whenever I had to target elements inside demo containers. So, I came up with a hack whereby I assigned the expression to a local
With this in place,
Note that I have enclosed the demo's script contents in an immediately invoked function expression (IIFE), so that the
Where ECMAScript6 is available, it's possible to achieve localization using "block scoping," with just braces enclosing
Of course, all of the above is only possible where shadow DOM version 1 is supported. Chrome, Safari, Opera and Android all look pretty good, but Firefox and Microsoft browsers are problematic. It is possible to feature-detect support and provide an error message where
Or you can include Shady DOM and the Shady CSS extension, which means a somewhat large dependency (60 KB+) and a different API. Rob Dodson was kind enough to provide me with a
basic demo
, which I'm happy to share to help you get started.
With the basic inline demo functionality in place, quickly writing working demos inline with their documentation is mercifully straightforward. This affords us the luxury of being able to ask questions like, "What if I want to provide a caption to label the demo?" This is perfectly possible already since — as previously noted — Markdown supports raw HTML.
However, the only new part of this amended structure is the wording of the caption itself. Better to provide a simple interface for supplying it to the output, saving my future self — and anyone else using the shortcode — time and effort and reducing the risk of coding typos. This is possible by supplying a named parameter to the shortcode — in this case, simply named
Named parameters are accessible in the template like
Here's how the full
One last note: Should I want to support markdown syntax in the caption value, I can pipe it through Hugo's
For its performance and its many excellent features, Hugo is currently a comfortable fit for me when it comes to static site generation. But the inclusion of shortcodes is what I find most compelling. In this case, I was able to create a simple interface for a documentation issue that I've been trying to solve for some time.
As in web components, a lot of markup complexity (sometimes exacerbated by adjusting for accessibility) can be hidden behind shortcodes. In this case, I'm referring to my inclusion of
I believe shortcodes are to Markdown and content what web components are to HTML and functionality: a way to make authorship easier, more reliable and more consistent. I look forward to further evolution in this curious little field of the web.
initial
is quite… idiosyncratic. In practice, all of the affected elements go back to adopting just their user agent styles (like
display: block
for
elements). However, the element to which it is applied —
class=“demo”
— needs to have certain user agent styles explicitly reinstated. In our case, this is just
display: block
, since
class=“demo”
is a
.demo { all: initial; display: block; }
all
is so far not supported in Microsoft Edge but is under consideration. Support is, otherwise,
reassuringly broad
. For our purposes, the
revert
value would be more robust and reliable but it is not yet supported anywhere.
Shadow DOM’ing The Shortcode
all: initial
does not make our inline components completely immune to outside influence (specificity still applies), but we can be confident that styles are unset because we are dealing with the reserved
demo
class name. Mostly just inherited styles from low-specificity selectors such as
html
and
body
will be eliminated.
element. I'd like to be able to simply write something like the following, without fear that the
button
element selector will apply to
elements in the pattern library itself or in other components in the same library page.
{{
{{ .Inner }}
part of the shortcode template and include it as the
innerHTML
of a new
ShadowRoot
. I might implement this like so:
{{ $uniq := .Inner | htmlEscape | base64Encode | truncate 15 "" }}
$uniq
is set as a variable to identify the component container. It pipes in some Go template functions to create a unique string… hopefully(!) — this isn't a bulletproof method; it's just for illustration.
root.attachShadow
makes the component container a shadow DOM host.
innerHTML
of the
ShadowRoot
using
{{ .Inner }}
, which includes the now-encapsulated CSS.
Permitting JavaScript Behavior
innerHTML
is not parsed or executed. This can be solved by importing from the content of a
element. I amended my implementation accordingly.
{{ $uniq := .Inner | htmlEscape | base64Encode | truncate 15 "" }}
{{ .Inner }}
{{
JavaScript Encapsulation
[aria-pressed]
button in the parent page before this component's example, then
document.querySelector
would target that instead.
document
for just the demo's subtree. This is definable, albeit quite verbosely:
document.getElementById('demo-{{ $uniq }}').shadowRoot;
demo
variable and prefixed scripts supplied via the shortcode with this assignment:
if (script) { script.textContent = `(function() { var demo = document.getElementById(\'demo-{{ $uniq }}\').shadowRoot; ${script.textContent} })()` } root.shadowRoot.appendChild(document.importNode(template.content, true));
demo
becomes the equivalent of
document
for any component subtrees, and I can use
demo.querySelector
to easily target my toggle button.
var toggle = demo.querySelector('[aria-pressed]');
demo
variable — and all proceeding variables used for the component — are not in the global scope. This way,
demo
can be used in any shortcode's script but will only refer to the shortcode in hand.
let
or
const
statements. However, all other definitions within the block would have to use
let
or
const
(eschewing
var
) as well.
{ let demo = document.getElementById(\'demo-{{ $uniq }}\').shadowRoot; // Author script injected here }
Shadow DOM Support
attachShadow
is not available:
if (document.head.attachShadow) { // Do shadow DOM stuff here } else { root.innerHTML = 'Shadow DOM is needed to display encapsulated demos. The browser does not have an issue with the demo code itself'; }
Captions For Components
caption
:
{{
{{ .Get "caption" }}
, which is simple enough. I want the caption and, therefore, the surrounding
and
to be optional. Using
if
clauses, I can supply the relevant content only where the shortcode provides a caption argument:
{{ if .Get "caption" }}
demo.html
template now looks (admittedly, it's a bit of a mess, but it does the trick):
{{ $uniq := .Inner | htmlEscape | base64Encode | truncate 15 "" }} {{ if .Get "caption" }}
markdownify
function. This way, the author is able to supply markdown (and HTML) but is not forced to do either.
{{ .Get "caption" | markdownify }}
Conclusion
role="group"
and the
aria-labelledby
relationship, which provides a better supported "group label" to the
— not things that anyone relishes coding more than once, especially where unique attribute values need to be considered in each instance.
Resources
(vf, al, il)