Twig Sprint Summary, BADCamp 2012

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
You are viewing a wiki page. You are welcome to join the group and then edit it. Be bold!

UPDATE: Looking to help with Twig tasks?

Please see the Twig sandbox for all information about the roadmap, current tasks, related issues, and more.

We're sprinting on Twig, the new theme layer for Drupal 8 that was just committed yesterday! In attendance, are moretendk, JohnAlbin, jenlampton, c4rl, ry5n, snugug, dnotes, philosurfer, mikestewart, laura s, eojthebrave, eatings, chrisshattuck, dboeger, ceagans, steveoliver, starryeyez024, and others.

We decided to work on several different things today: we have one group of people converting theme functions to template files, and we have a larger group discussing HTML patterns in core, and coming up with our component library, based on Jacine's Blog post and her code example.

Most of the sprint was a discussion about our approach and principles to guide the ongoing Twig tasks.

Approach: A new theme in core

The first question I asked was if it was "okay" to break all the CSS and JS in core by reworking markup, and this brought up two points of view.

  • Core default markup in D7 is bloated: We should strip it down entirely.
  • Core default markup in D7 contains some useful things (classes, IDs, wrappers) that certain themers might still want to keep. We should just move it someplace else.

As we discussed why people wanted option (1) (minimal markup and CSS in core default templates), we all started to agree that this would be a very attractive thing for front-end developers, but that we still need option (2) to demonstrate some useful markup and classes for people to use who were CSS savvy.

It became apparent that Stark in D7 does two things: It represents a window into core default markup. But the result of this markup currently has some feasibly beneficial classes and IDs for people to use as a potential base theme. Our proposition for Drupal 8 is that these should be separated. Core default markup would be essentially "naked" so that no extraneous markup exists in the templates and there exists an optional core theme that provides the possibly-useful classes and IDs (for example, .node, .block, .front, etc).

The working title for this new theme as conceived in the sprint is "Lush." It is possible that it is simply a refactored Bartik.

Drupal 7

Core Stark
Markup
Classes
IDs
(JS?)
.info

Drupal 8

Core Stark Codename "Lush"*
Markup (minimal) .info .info
Markup (rich)
Classes
IDs
(JS?)

*Possibly a refactored Bartik.

We didn't get into the specifics of how JavaScript would work, so that is a discussion yet to happen.

With this approach we wish to appeal to two personae:

Persona A: Front-end expert (Stark)
The goal of Stark in D8 retains a focus on minimal output. Front-end experts who prefer very clean semantic markup will prefer these templates as a base for a custom theme. This way, they don't have to work backwards. This could improve Drupal's reputation among front-end experts outside of the community. Stark is not born out of practicality in a real-world use case, but rather to provide a clean, semantic slate.
Persona B: CSS-driven (Codename "Lush")
Some folks don't mind having some useful classes provided for them, and are likely to add only a style sheet that adds or changes CSS from core. These themers would use Codename "Lush" as a base theme. This would preserve some purpose and utility of long-standing class and markup conventions in Drupal.

Given the existing prospect of implementing a Theme Component Library (TBD – #1804488), our hope is core modules will not need to provide their own markup, they can use the components from core (mostly). Those components can be overridden in the Lush theme to add useful divs, IDs or classes.

What about module preprocess functions that add classes?

Certain modules provide preprocessors to provide classes for the markup that they define in hook_theme(). An example of this is template_preprocess_node() in node.module. Classes like .promoted, .sticky, .unpublished, and others are added to the template wrapper.

These are definitely not mandatory from a functionality standpoint, they are just nice-to-haves for themes. Removing these classes doesn't break anything critical (that is, removing .unpublished doesn't affect node_access), but they can be useful to have and there are definite general use cases where they apply.

We came to the conclusion that many of these can remain as-is, and that the majority of clean-up should consist of cleaning up extraneous markup and classes in the templates themselves (as these get the biggest gripes from front-end experts).

Based on the principle of "Build from use cases," we want to accommodate 90% of what people will use and provide tools for the remaining 10% of use cases.

So, consider the following example. Suppose our Codename "Lush" theme is being used as a base theme, and the .sticky class is added by the base template_preprocess_node() function and node.html.twig that starts something like this:

Twig template:
<article class="node node-{{ node.type }} {{ attributes.class }}"{{ attributes }}>
...

Sample resulting markup:
<article class="node node-article sticky" data-nid="123">

So, the "sticky" class lives in attributes.class (added by the preprocessor). It seems useful here to make the distinction that sticky is a property that any node may have and that the appearance of a sticky node being different than a non-sticky node is a general use case whereas the appearance of .node-article vs .node-page may not. (@todo Consider whether we could explore the distinction of "functional" classes vs "appearance" classes, or use data- attributes for things like sticky/published/etc).

Suppose someone uses the template as a base for their theme and wants to clean-up classes. At minimum, they could do the following:

  Twig template:
  <article{{ attributes }}>
  ...

  Sample resulting markup:
  <article class="sticky" data-nid="123">

So, .sticky being a functional HTML class added by the preprocessor remains since it is part of the attributes object. We believe this covers the 90% of use cases.

Based on the principle of "Provide tools," in the event that someone really wants to remove this class, they have options. They can remove all classes in their custom preprocessor function, or simply unset the class variable in the twig template (See http://drupal.org/node/1696912#comment-6704868, syntax uncertain. @todo provide updated syntax)

  (Option i) Preprocess function:
  mytheme_preprocess_node(&$vars) {
    unset($vars['attributes']->class);
  }

  (Option ii) Twig template:
  {% unset attributes.class %}
  <article{{ attributes }}>
  ...

  Sample resulting markup:
  <article data-nid="123">

What about contrib?

Contrib modules must provide certain default styling to be functional for site builders that want to download the module and use it right away. Consider the fivestar module: the styling and graphics must work out of the box. So, contrib modules should, minimally, always provide appropriate markup, IDs, classes, CSS and JS. This assures the least sophisticated Drupal user can install a module and get functionality out of the box, and Persona B can leverage existing markup.

For Persona A, we want to provide a mechanism in a custom theme to disable the CSS files and JS files added by contrib modules (some sort of simple asset management). This makes overriding contrib styling easier: instead of copying an entire CSS file and then changing all the properties, we can simply exclude the file from being included in the first place and write our own CSS.

For example, a custom theme could implement the following syntax in the .info file. These are examples in order from most granular to least granular (Arguably, the latter examples may be overkill).

  ; Use case i.
  ; Use sub elements to exclude individual CSS or JS files.
  assets[fivestar][base.theme.css] = FALSE
  assets[fivestar][admin.theme.css] = FALSE
  assets[fivestar][fivestar.js] = FALSE

  ; Use case ii.
  ; Exclude all CSS or JS files from a contrib.
  assets[fivestar][css] = FALSE
  assets[fivestar][js] = FALSE

  ; Use case iii.
  ; Exclude all assets (both CSS and JS files) from a contrib.
  assets[fivestar] = FALSE

  ; Use case iv.
  ; By default, we'll have a whitelist policy: all assets will be included
  ; and we'll want to exclude them on a case-by-case basis. However, we
  ; could change the default so that all assets are excluded by default,
  ; and we can include them on a case-by-case basis.
  ; Disable all contrib CSS files by default and perhaps simply enable fivestar's CSS
  assets[contrib][css] = FALSE
  assets[fivestar][css] = TRUE

Issue: http://drupal.org/node/1833896

Principles: Waypoints to guide us

After we established this approach, we decided to focus on defining some core principles to guide the work necessary to convert and revise existing theme templates and functions to D8.

Start with nothing
Core default markup should strive for semantic simplicity, with few HTML elements, added only as needed
Build from use cases
Don't assume you know what people want or add features based on "What-if?" Think about the 90% of use cases.
Provide tools
Give front-end experts a way to achieve specific goals; goals that apply to the remaining 10% of use cases. Keep in mind that complex problems may require complex solutions.
Consolidate
"Your markup is not special." Don't make something new. Work toward a Theme Component Library that modules leverage.
Visibility
You should be able to see what's going on in a template without reading docs. Twig provides a lot of this by virtue of its syntax (it looks like HTML). Form follows function.
Consistency
Do the same things everywhere, follow patterns. Use similar variable names across templates if they represent similar things.
Don't dumb it down
Complexity should be reduced but not obscured. Themers *can* understand template logic and loops. When complexity does happen, use comments to explain why.
Organization should be driven by meaning and semantics over technical convenience
Consider what an element means rather than how it structurally appears. For example: theme_item_list() came about from a developer's perspective: OL and UL markup is nearly structurally the same, so providing a single function with the type argument came about from a code efficiency standpoint. However, from a semantic HTML perspective, we do not use a singular 'list' element with an attribute to indicate whether it is ordered or unordered, we have two different elements. Semantically, OL and UL represent different information. Therefore, we should provide a separate templates for OL and UL, even though they contain nearly identical markup.
Related to this principle (and the principle of Visibility), names and locations of templates should be self-evident. Consider where a newcomer might expect to override markup. Example: Someone looking to override a menu isn't going to look for a item_list template, even if a menu is structurally identical. So we should not simply have a menu use an item list, nor should we simply include an item list from a menu template. Themers want to see markup in templates, not abstraction.

Markup

After establishing these principles, we proceeded to look at some template examples and work on rewriting them according to the principles. Some of the above principles were added and revised during this process. We mainly focused on basic examples toward the pursuit of creating theme components.

We worked quite a bit on item-list.html.twig, adding ol.html.twig and ul.html.twig, then looked at menu.html.twig. These patches are currently on jenlampton's local machine, but we'll be looking to get these posted into the existing issues. (@todo get jenlamptons patches)

It was an enriching process to have many voices and perspectives in the room when writing template code.

Revised APIs

We didn't get much into revised PHP tooling. Much of this is TBD.

Markup utility functions: theme-less markup

jenlampton and c4rl made an important realization at the end of the sprint. Certain existing theme functions simply output a single element. Some of these looked like this:

  {# link.html.twig -- refactoring theme_link() #}
  <a href="{{url}}"{{attributes}}>{{text}}</a>

  {# img.html.twig -- refactoring theme_image() #}
  <img{{attributes}}/>

As we looked at these, they were suspiciously similar and simple. We began to question the use and purpose of these:

  • How are these useful to themers? Are they at all?
  • Why do they go through the theme layer? What was the origin of this?
  • What is a use case where a themer would have to override every image tag, or every link? Keep in mind we're designing for majority use cases.
  • Why do we have theme_link and theme_image when we don't have theme_paragraph, theme_blockquote, theme_heading, etc?
  • What can we learn from l(), a function that produces markup that doesn't go through the theme layer?

Functions like l(), theme_image(), and theme_image_style() are used because they are shortcuts to write tags. They offer developers more benefit than themers as they are a shortcut to write common markup.

The only point of sending markup through the theme layer is for themers to override that markup. After the sprint, effulgentsia mentioned that something like theme_link() was added out of a blanket policy that "All markup should go through the theme layer." Given that we were questioning many things at the sprint, we asked "Is this policy is too general?"

We couldn't think of a general use case where overriding every single link tag or every single image tag would be useful. Generally speaking, we believe if the markup of a specific link or image tag needs to be customized, that element is part of a larger structure (such as a pager or a menu) and the specifics of that link can be addressed via the parent template.

So, an issue was proposed to as "What if we had some markup bypass the theme layer?" (see http://drupal.org/node/1833920). UPDATE: After much discussion, some great realizations were made about the purpose and utility of things like theme_link() and l(), but the need for addressability (via array structure), and the prospect of a link being actually a Theme Component (say, a simplified button design pattern) clarified the necessity. Please see the linked issue for the full discussion.

JavaScript class selectors overlapping with CSS

We want to remove some markup and classes, but not that which would affect functionality via JavaScript too terribly. There's a lot of gray area here, but some solutions could be class namespacing or usage of data- attributes.

Issue: http://drupal.org/node/1833912

Next steps

@todo Figure out which templates that have already been converted to Twig simply "work" in twig, or if they have to be refactored, i.e. where a theme_foo() is renamed to template_preprocess_foo() and we have an empty template with {{ content }}
@todo Consider whether we should setup gatekeepers for the patches
@todo How do HTML5 semantics affect Stark?

AttachmentSize
d8-theming-approach-whiteboard-20121104.jpg2.26 MB
DSC_0352.jpg381.25 KB
DSC_0351.jpg204.88 KB
DSC_0350.jpg174.29 KB
DSC_0348.jpg172.57 KB
DSC_0347.jpg170.26 KB

Comments

There are a lot of good ideas

DesignDolphin's picture

There are a lot of good ideas here. The principles are spot on. Really glad to see these.

Contrib modules should always always write to the Lush perspective, adding CSS and JS that target the HTML Structure IDs and classes that are provided there. This assures the least sophisticated Drupal user can install a module and get functionality out of the box, but we will also provide a mechanism for the advanced theme developer to disable the CSS and JS added by contrib modules.

I want to touch on this for a minute. I agree with the advantages of having drop in code as well as being able to roll your own 100%.

Are we asking too much of module developers to do this for free?
Take a look at base themes for example and their differences. In themes some may want data shown in a table, others in a CSS table, and yet others again as XML. How do you know the standard?

Some developers already feeling the strain of all the changes and growth of Drupal. The module author is in danger of becoming a slave to all, master of none. Right now Twig is hot, a few years ago it was RSS and Javascript, what is the next 'must have' hot feature?

Here are just some ideas that I'd like to enter into the workgroup for consideration:

  • Make templates for a module in a plugin architecture. Keep the bare bones in the main module. Adding different theme capabilities in a theme plugin folder. Encourage sharing of those. This will make it easier to switch out/ add a new functionality without having to change the core module.
  • Front End developers should be responsible for (finding the resources for) adding different templates to a module besides the core, unless the module developer already wants to build the functionality.
  • If you want a certain template feature for a module hire a module author/ developer to write this for the plugin. This could be done by crowd sourcing as well.
  • If a front-end developer makes money for building a website using a module it should be standard common courtesy to donate some of the income to the module developer, or donate module template code back to the module. Personal sites could be excluded from this (if not making any momey).
  • Have something like Ubuntu ideas to allow people to vote on popular theme features for a feature, assemble people (and funds) to do this and build it.
  • Have code snippets in the documentation

Writing contrib code should be fun, not become a hellish chore. Where module authors would be exposed to the whim or hot design feature of the day, and get blamed if it doesn't work or not kept up. Keeping a lean module core also allows for keeping the bug count down and maintainability up.

Definitely you could have dev teams around a module. I could see some awesome stuff coming out of that.

Keeping module and front end developers happy will definitely help with acceptance and growth of this. What I'm hoping for is a balanced and level playing field.

Don't follow

steveoliver's picture

DesignDolphin,

I really don't understand what you're getting at in your comment.

I think the point was instead

jenlampton's picture

I think the point was instead of having module developers design for lush, DesignDolphin wanted them to design against core, (or stark).

The problem with that is that there will be no IDs or Classes in core/stark, so there won't be any identifiers there for contrib to write styles against. Lush will have IDs and Classes that would make it easy for contrib to use. So module maintainers can call theme('item_list) and write a style sheet to style the output.

I do like the idea of having templates + styles for contrib that can be shared too. If we can make the theme system modular enough, we'd end up with little mini-themes for each module. Fivestar can ship with whatever it has, but then theme developers can add their own templates + styles, and contribute those back too. (We'd probably need some changes d.o to make this clearer - but it is already possible)

Second edition

c4rl's picture

I edited the node here to fully explain a lot of the ideas in more detail since there has been some confusion, so for those following along via email notifications, please re-read if you are interested.

adamdicarlo's picture

[...] one group of people converting theme functions to template files, and we have a larger group discussing HTML patterns in core [...]

Er... because everyone else seemed to have discussion covered and nobody was working on the back-end, I figured I could provide the most help there. I think nobody knew I was working on the back-end except Jen, though.

What happened was the twig_engine branch of the sandbox was deprecated but the sandbox page Jen pointed me to didn't/doesn't say that. So I worked on the branch and found core wouldn't install, which seemed like an important bug... so I tracked down why and wrote a patch and then started to realize that the bug was just a symptom of huge problems that were probably irrelevant to what had already been committed to core. But I didn't realize that until I had to leave to catch my plane.

The whiteboarding discussion about markup was kind of intimidating - I got the feeling no one wanted to break from it for a minute to provide guidance, and that the people who could provide guidance were all there.

So my actual-coding-to-help experience was frustrating, though listening to the debate about templates and markup, and talking to people before that got started, was very interesting and fun.

Anyway, I'm hoping relaying this experience will help in the future with coordinating all of the people helping, and help others like me realize to check in frequently with someone about your progress and what you're doing. Even if it means annoying them by basically interrupting their discussion, I guess. Just do it early, and ask for someone to break off and provide guidance.

Well, as I already said. I

Fabianx's picture

Well, as I already said.

I apologize for that. I missed your questions in #drupal-contribute and #drupal-twig by minutes.

Normally the twig_engine branch would be the right branch to work in.

I did not realize that it did no longer install, because I usually keep things pretty stable. Getting this into core was a huge goal and I used the twig_engine branch to test engine features with fully converted templates.

We should have a stable code base soon once I have re-based on everything.

If you are still interested to help, catch me in #drupal-twig.

Thanks,

Fabian

Thanks.

adamdicarlo's picture

Please don't worry, no need to apologize again. It was (I think/hope) partly just a perfect storm of things going wrong. I still want to help! My comment wasn't really to complain, just wanted to communicate and show what my experience was, hopefully help future contributors and organizers. I don't at all regret coming to the code sprint! This was my first core code sprint and I still have a positive outlook for future efforts and excitement about Twig in core. :) By the way— thank you and everyone else for all your work on Twig in Drupal so far!

Thanks for attending the

c4rl's picture

Thanks for attending the sprint, and thanks for sharing your comments. The input and feedback is really valuable for everyone.

I will definitely agree with you that the current state of the documentation regarding next steps and agenda is fragmented and I want to spend some time cleaning this up to prevent the sort of misdirection you encountered.

It's true that discussion dominated the sprint rather than writing code. What you'll may discover is that sometimes the best sprints are not ones that you spend writing code, but rather ones where you spend discussing some of the direction and larger issues at hand.

Many problems we face aren't simply fixed by writing code from the start, but first establishing an understanding of the problems, and an approach to fix them. These things are much easier to establish in-person rather than the issue queue, whereas writing code and posting patches after the direction has been established is often better served by the issue queue. So, often at a "code sprint," we don't write much code; we discuss. :)

Thanks again for your response, I look forward to working with you on the Twig efforts.

More on preprocessors

c4rl's picture

I edited the node to explain "What about module preprocess functions that add classes?" discussed at the Sprint. Please see the updated edition for details.