Tag: Strategies

Here’s How I Solved a Weird Bug Using Tried and True Debugging Strategies

Remember the last time you dealt with a UI-related bug that left you scratching your head for hours? Maybe the issue was happening at random, or occurring under specific circumstances (device, OS, browser, user action), or was just hidden in one of the many front-end technologies that are part of the project?

I was recently reminded of how convoluted UI bugs can be. I recently fixed an interesting bug that was affecting some SVGs in Safari browsers with no obvious pattern or reason. I had searched for any similar issues to get some clue about what was going on, but I found no useful results. Despite the obstacles, I managed to fix it.

I analyzed the problem by using some useful debugging strategies that I’ll also cover in the article. After I submitted the fix, I was reminded of the advice Chris has tweeted out a while back.

…and here we are.

Here’s the problem

I found the following bug on a project I’ve been working on. This was on a live site.

I reproduced this issue with any paint event, for example, resizing the screen.

I created a CodePen example to demonstrate the issue so you can check it out for yourself. If we open the example in Safari, the buttons might look as expected on load. But if we click on the first two larger buttons, the issue rears its ugly head. 

Whenever a browser paint event happens, the SVG in the larger buttons render incorrectly. It simply gets cut off. It might happen randomly on load. It might even happen when the screen is resized. Whatever the situation, it happens!

Ugh, why is the SVG icon getting cut off?!

Here’s how I approached the problem.

First off, let’s consider the environment

It’s always a good idea to go over the details of the project to understand the environment and conditions under which the bug is present.

  • This particular project is using React (but is not required for following this article).
  • The SVGs are imported as React components and are inlined in HTML by webpack.
  • The SVGs have been exported from a design tool and have no syntax errors.
  • The SVGs have some CSS applied to them from a stylesheet.
  • The affected SVGs are positioned inside a <button> HTML element.
  • The issue occurs only in Safari (and was noticed on version 13).

Down the rabbit hole

Let’s take a look at the issue and see if we can make some assumptions about what is going on. Bugs like this one get convoluted, and we won’t immediately know what is going on. We don’t have to be 100% correct in our first try because we’ll go step-by-step and form hypotheses that we can test to narrow down the possible causes. 

Forming a hypothesis

At first, this looks like a CSS issue. Some styles might be applied on a hover event that breaks the layout or the overflow property of the SVG graphic. It also looks like the issue is happening at random, whenever Safari renders the page (paint event when resizing the screen, hover, click, etc.).

Let’s start with the simple and most obvious route and assume that CSS is the cause of the issue. We can consider the possibility that there is a bug in the Safari browser that causes SVG to render incorrectly when some specific style applies to the SVG element, like a flex layout, for example.

By doing so, we’ve formed a hypothesis. Our next step is to set up a test that is either going to confirm or contradict the hypothesis. Each test result will produce new facts about the bug and help form further hypotheses. 

Problem simplification

We’ll use a debugging strategy called problem simplification to try and pinpoint the issue. Cornell University’s CS lecture describes this strategy as “an approach to gradually eliminate portions of the code that are not relevant to the bug.”

By assuming the issue lies within CSS, we can end up pinpointing the issue or eliminating the CSS from the equation, reducing the number of possible causes and the complexity of the problem. 

Let’s try and confirm our hypothesis. If we temporarily exclude all non-browser stylesheets, the issue should not occur. I did that in my source code by commenting out the following line of code in my project.

import 'css/app.css';

I have created a handy CodePen example to demonstrate these elements without CSS included. In React, we are importing SVG graphics as components, and they are inlined in HTML using webpack.

If we open this pen on Safari and click on the button, we are still getting the issue. It still happens when the page loads, but on CodePen we have to force it by clicking the button. We can conclude that the CSS isn’t the culprit, but we can also see that only the two out of five buttons break under this condition. Let’s keep this in mind and move on to the next hypothesis.

The same SVG elements still break with excluded stylesheets (Safari 13)

Isolating the issue

Our next hypothesis states that Safari has a bug when rendering SVG inside an HTML <button> element. Since the issue has occurred on the first two buttons, we’ll isolate the first button and see what happens.

Sarah Drasner explains the importance of isolation and I highly recommend reading her article if you want to learn more about debugging tools and other approaches.

Isolation is possibly the strongest core tenets in all of debugging. Our codebases can be sprawling, with different libraries, frameworks, and they can include many contributors, even people who aren’t working on the project anymore. Isolating the problem helps us slowly whittle away non-essential parts of the problem so that we can singularly focus on a solution.

A “reduced test case” it is also often called. 

I moved this button to a separate and empty test route (blank page). I created the following CodePen to demonstrate that state. Even though we’ve concluded that the CSS is not the cause of the issue, we should keep it excluded until we’ve found out the real cause of the bug, to keep the problem simple as possible.

If we open this pen in Safari, we can see that we can no longer reproduce the issue and the SVG graphic displays as expected after clicking the button. We shouldn’t consider this change as an acceptable bug fix, but it gives a good starting point in creating a minimal reproducible example.

A minimal reproducible example

The main difference between the previous two examples is the button combination. After trying out all possible combinations, we can conclude that this issue occurs only when a paint event occurs on a larger SVG graphic that is alongside a smaller SVG graphic on the same page.

We created a minimal reproducible example that allows us to reproduce the bug without any unnecessary elements. With minimal reproducible example, we can study the issue in more detail and accurately pinpoint the part of the code causing it.

I’ve created the following CodePen to demonstrate the minimal reproducible example.

If we open this demo in Safari and click on a button, we can see the issue in action and form a hypothesis that these two SVGs somehow conflict with one another. If we overlay the second SVG graphic over the first, we can see that the size of the cropped circle on the first SVG graphic matches the exact dimensions of the smaller SVG graphic.

Edited image that compares the smaller SVG graphic to the first SVG graphic with the bug present

Divide and conquer

We’ve narrowed down the issue to the combination of two SVG graphics. Now we’re going to narrow things down to the specific SVG code that’s messing things up. If we only have a basic understanding of SVG code and want to pinpoint the issue, we can use a binary tree search strategy with a divide-and-conquer approach. Cornell University’s CS lecture describes this approach:

For example, starting from a large piece of code, place a check halfway through the code. If the error doesn’t show up at that point, it means the bug occurs in the second half; otherwise, it is in the first half. 

In SVG, we can try deleting <filter> (and also <defs> since it’s empty anyway) from the first SVG. Let’s first check what <filter> does. This article by Sara Soueidan explains it best.

Just like linear gradients, masks, patterns, and other graphical effects in SVG, filters have a conveniently-named dedicated element: the <filter> element.

A <filter> element is never rendered directly; its only usage is as something that can be referenced using the filter attribute in SVG, or the url() function in CSS.

In our SVG, <filter> applies a slight inset shadow at the bottom of the SVG graphic. After we delete it from the first SVG graphic, we expect the inner shadow to be gone. If the issue persists, we can conclude that something is wrong with the rest of the SVG markup.

I’ve created the following CodePen to showcase this test.

As we can see, the issue persists anyway. The inset bottom shadow is displayed even though we’ve removed the code. Not only that, now the bug appears on every browser. We can conclude that the issue lies within the rest of the SVG code. If we delete the remaining id from <g filter="url(#filter0_ii)">, the shadow is fully removed. What is going on?

Let’s take another look at the previously mentioned definition of the <filter> property and notice the following detail:

A <filter> element is never rendered directly; its only usage is as something that can be referenced using the filter attribute in SVG.

(Emphasis mine)

So we can conclude that the filter definition from the second SVG graphic is being applied to the first SVG graphic and causing the error.

Fixing the issue

We now know that issue is related to the <filter> property. We also know that both SVGs have the filter property since they use it for the inset shadow on the circle shape. Let’s compare the code between the two SVGs and see if we can explain and fix the issue.

I’ve simplified the code for both SVG graphics so we can clearly see what is going on. The following snippet shows the code for the first SVG.

<svg width="46" height="46" viewBox="0 0 46 46">   <g filter="url(#filter0_ii)">     <!-- ... -->   </g>   <!-- ... -->   <defs>     <filter id="filter0_ii" x="0" y="0" width="46" height="46">       <!-- ... -->     </filter>   </defs> </svg>

And the following snippet shows the code for the second SVG graphic.

<svg width="28" height="28" viewBox="0 0 28 28">   <g filter="url(#filter0_ii)">     <!-- ... -->   </g>   <!-- ... -->   <defs>     <filter id="filter0_ii" x="0" y="0" width="28" height="28">       <!-- ... -->     </filter>   </defs> </svg>

We can notice that the generated SVGs use the same id property id=filter0_ii. Safari applied the filter definition it read last (which, in our case, is the second SVG markup) and caused the first SVG to become cropped to the size of the second filter (from 46px to 28px). The id property should have a unique value in DOM. By having two or more id properties on a page, browsers cannot understand which reference to apply, and the filter property redefines on each paint event, dependent on the racing condition that causes the issue to appear randomly.

Let’s try assigning unique id attribute values to each SVG graphic and see if that fixes the issue.

If we open the CodePen example in Safari and click the button, we can see that we fixed the issue by assigning a unique ID to <filter> property in each SVG graphic file. If we think about the fact that we have non-unique value for an attribute like id, it means that this issue should be present on all browsers. For some reason, other browsers (including Chrome and Firefox) seem to handle this edge-case without any bugs, although this might be just a coincidence.

Wrapping up

That was quite a ride! We started barely knowing anything about an issue that seemingly occurred at random, to fully understanding and fixing it. Debugging UI and understanding visual bugs can be difficult if the cause of the issue is unclear or convoluted. Luckily, some useful debugging strategies can help us out.

First, we simplified the problem by forming hypotheses which helped us eliminate the components that were unrelated to the issue (style, markup, dynamic events, etc.). After that, we isolated the markup and found the minimal reproducible example which allowed us to focus on a single chunk of code. Finally, we pinpointed the issue with a divide-and-conquer strategy, and fixed it.

Thank you for taking the time to read this article. Before I go, I’d like to leave you with one final debugging strategy that is also featured in Cornell University’s CS lecture

Remember to take a break, relax and clear your mind between debugging attempts.

 If too much time is spent on a bug, the programmer becomes tired and debugging may become counterproductive. Take a break, clear your mind; after some rest, try to think about the problem from a different perspective.

References


The post Here’s How I Solved a Weird Bug Using Tried and True Debugging Strategies appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , , ,

Some CSS Grid Strategies for Matching Design Mockups

The world of web development has always had a gap between the design-to-development handoff. Ambitious designers want the final result of their effort to look unique and beautiful (and true to their initial vision), whereas many developers find more value in an outcome that is consistent, dependable, and rock solid (and easy to code). This dynamic can result in sustained tension between the two sides with both parties looking to steer things their own way.

While this situation is unavoidable to some extent, new front-end technology can play a role in bringing the two sides closer together. One such technology is CSS grid. This post explores how it can be used to write CSS styles that match design layouts to a high degree of fidelity (without the headache!).

A common way that designers give instructions to front-end developers is with design mockups (by mockups, we’re talking about deliverables that are built in Sketch, XD, Illustrator, Photoshop etc). All designers work differently to some degree (as do developers), but many like to base the structure of their layouts on some kind of grid system. A consistent grid system is invaluable for communicating how a webpage should be coded and how it should respond when the size of the user’s screen differs from the mockup. As a developer, I really appreciate designers who take the trouble to adopt a well thought-out grid system.

A 12-column layout is particularly popular, but other patterns are common as well. Software like Sketch and XD makes creating pages that follow a preset column layout pretty easy — you can toggle an overlay on and off with the click of a button.

A grid layout designed in Sketch (left) and Adobe XD (right)

Once a grid system is implemented, most design elements should be positioned squarely within it. This approach ensures that shapes line up evenly and makes for a more appealing appearance. In addition to being visually attractive, a predictable grid gives developers a distinct target to shoot for when writing styles.

Unfortunately, this basic pattern can be deceptively difficult to code accurately. Frameworks like Bootstrap are often used to create grid layouts, but they come with downsides like added page weight and a lack of fine-grained control. CSS grid offers a better solution for the front-end perfectionist. Let’s look at an example.

A 14-column grid layout

The design above is a good application for grid. There is a 14-column pattern with multiple elements positioned within it. While the boxes all have different widths and offsets, they all adhere to the same grid. This layout can be made with flexbox — and even floats — but that would likely involve some very specific math to get a pixel-perfect result across all breakpoints. And let’s face it: many front-end developers don’t have the patience for that. Let’s look at three CSS grid layout strategies for doing this kind of work more easily.

Strategy 1: A basic grid

See the Pen
Basic Grid Placement
by chris geel (@RadDog25)
on CodePen.

The most intuitive way to write an evenly spaced 12-column layout would probably be some variation of this. Here, an outer container is used to control the outside gutter spacing with left and right padding, and an inner row element is used to restrain content to a maximum width. The row receives some grid-specific styling:

display: grid; grid-template-columns: repeat(12, 1fr); grid-gap: 20px;

This rule defines the grid to consist of 12 columns, each having a width of one fractional unit (fr). A gap of 20px between columns is also specified. With the column template set, the start and end of any child column can be set quite easily using the grid-column property. For example, setting grid-column: 3/8 positions that element to begin at column three and span five columns across to column eight.

We can already see a lot of value in what CSS grid provides in this one example, but this approach has some limitations. One problem is Internet Explorer, which doesn’t have support for the grid-gap property. Another problem is that this 12-column approach does not provide the ability to start columns at the end of gaps or end columns at the start of gaps. For that, another system is needed.

Strategy 2: A more flexible grid

See the Pen
More Flexible Grid Placement
by chris geel (@RadDog25)
on CodePen.

Although grid-gap may be a no go for IE, the appearance of gaps can be recreated by including the spaces as part of the grid template itself. The repeat function available to grid-template-columns accepts not just a single column width as an argument, but repeating patterns of arbitrary length. To this end, a pattern of column-then-gap can be repeated 11 times, and then the final column can be inserted to complete the 12-column / 11 interior gap layout desired:

grid-template-columns: repeat(11, 1fr 20px) 1fr;

This gets around the IE issue and also allows for columns to be started and ended on both columns or gaps. While being a nice improvement over the previous method, it still has some room to grow. For example, what if a column was to be positioned with one side spanning to the outer edge of the screen, and the other fit within the grid system? Here’s an example:

A grid Layout with an that’s item flush to the outer edge

In this layout, the card (our left column) begins and ends within the grid. The main image (our right column) begins within the grid as well, but extends beyond the grid to the edge of the screen. Writing CSS for this can be a challenge. One approach might be to position the image absolutely and pin it to the right edge, but this comes with the downside of taking it out of the document flow (which might be a problem if the image is taller than the card). Another idea would be to use floats or flexbox to maintain document flow, but this would entail some tricky one-off calculation to get the widths and spacing just right. Let’s look at a better way.

Strategy 3: An even more flexible grid

See the Pen
Right Edge Aligned image with grid
by chris geel (@RadDog25)
on CodePen.

This technique builds on the idea introduced in the last revision. Now, instead of having the grid exist within other elements that define the gutter sizes and row widths, we’re integrating those spaces with the grid’s pattern. Since the gutters, columns, and gaps are all incorporated into the template, child elements can be positioned easily and precisely on the grid by using the grid-column property.

$ row-width: 1140px; $ gutter: 30px; $ gap: 20px; $ break: $ row-width + 2 * $ gutter; $ col-width-post-break: ($ row-width - 11 * $ gap) / 12;    .container {   display: grid;   grid-template-columns: $ gutter repeat(11, calc((100% - 2 * #{$ gutter} - 11 * #{$ gap})/12) #{$ gap}) calc((100% - 2 * #{$ gutter} - 11 * #{$ gap})/12) $ gutter;   @media screen and (min-width: #{$ break}) {     grid-template-columns: calc(0.5 * (100% - #{$ row-width})) repeat(11, #{$ col-width- post-break} #{$ gap}) #{$ col-width-post-break} calc(0.5 * (100% - #{$ row-width}));   } }

Yes, some math is required to get this just right. It’s important to have the template set differently before and after the maximum width of the row has been realized. I elected to use SCSS for this because defining variables can make the calculation a lot more manageable (not to mention more readable for other developers). What started as a 12-part pattern grew to a 23-part pattern with the integration of the 11 interior gaps, and is now 25 pieces accounting for the left and right gutters.

One cool thing about this approach is that it can be used as the basis for any layout that adheres to the grid once the pattern is set, including traditionally awkward layouts that involve columns spanning to outside edges. Moreover, it serves as a straightforward way to precisely implement designs that are likely to be handed down in quality mockups. That is something that should make both developers and designers happy!

There are a couple of caveats…

While these techniques can be used to crack traditionally awkward styling problems, they are not silver bullets. Instead, they should be thought of as alternative tools to be used for the right application.

One situation in which the second and third layout patterns are not appropriate are layouts that require auto placement. Another would be production environments that need to support browsers that don’t play nice with CSS grid.

The post Some CSS Grid Strategies for Matching Design Mockups appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Code as Documentation: New Strategies with CSS Grid

I work for Supercool, a fast-moving design agency that makes custom built sites for arts clients, powered by the off-the-shelf system, Craft CMS; it’s high-spec graphic design with relatively demanding typography and art direction. Over the past few months we’ve been moving to CSS grid. We’re transitioning slowly, allowing ourselves to discover new paradigms and design methods, instead of simply porting old habits to a new syntax.

So far, we’ve developed a number of really useful strategies for keeping track of the layout. I’ve written a couple of surprisingly nifty mixins, using named areas and templates, and we’ve hit upon some basic conventions to create highly readable code. I thought it would be valuable to walk through a fully-developed production implementation of a single major component using grid, digging in to some of the design questions it throws up and steering you away from some pitfalls we’ve encountered. CSS grid is a large spec, with lots of possible approaches and lots of right ways to do things, but at some point you have to lock down your method and get it live.

I’m expecting some basic familiarity with CSS, Sass, BEM, and some interest in the task of prototyping fully-realized, accessible, custom frameworks with 50+ components from Sketch or Photoshop-type documents on a tight timeline (say, a week).

First, let’s identify and separate out the design into distinct coding tasks and plan how we’ll approach them:

  1. Type: The designer has already defined a type system.
  2. Colors: First, we build a theme model and then include that in the partial.
  3. Content: What elements are in this block? What are its variations? This is where our BEM mixin comes in.
  4. Layout: This is how the content is placed in the block. You might want to skip directly to this.
  5. Conventions: This is exactly how we choose to write all the above. There are many right answers in CSS, so what is important is that we all just agree to a convention, the rules of the road. This really comes first, but for the sake of this article, we’ll conclude here.

Type system

We use utility classes (e.g. h-text--h1, h-text--badge) for type styles. There may be a hundred type styles in a project. We export those styles from Sketch right into our Patternlab using Typex. That’s a whole other article on its own, so let’s just stipulate type as handled. We won’t bring type into our component partial.

Color usage

See the Pen
CSS Variable fallbacks mixin v2
by limograf (@Sally_McGrath)
on CodePen.

Theming is a few tiny mixins dropped in, so we ideally won’t see a ton of color rules in our partial. We store them all together in a _themer.scss partial in our “Mixins and Models” library, so we can be sure to follow the design system of the site. This way, when someone comes back to the build later on, they have a key reference partial describing the design and branding rules. When building and maintaining numerous sites in broadly the same market — but each all with different brand spec — you’ve gotta make sure you don’t mix up one brand with another! So, much like type, we abstract the color rules away from the partial. In essence, we’re really only looking at layout (as much as possible) in our _header.scss file.

Given that we agree the convention to always theme using our mixin, this is how it would be included on an element:

@include var($ property, $ value);

Then we’ll set a theme model, of how colors work on this particular site and apply that theme to a component with:

@include theme;

Here’s the sample theme model we’re going to use with this page header. It’s super simple.

See the Pen
theme model
by limograf (@Sally_McGrath)
on CodePen.

We’re pairing a color with black or white. We depend on a contrast rule and flip them for emphasis, maybe on events, like hover, or a highlighted call to action. This is all we need to do to make that happen and now we have a document of how color should really work on this site. We can go to and check against if we need to debug or expand the UI.

We also want to prep inheritance to help us, so let’s identify some helpful conventions:

  • Set the fills on SVG icons to currentColor in your pipeline (and default size them as width: 1em; height: 1em; font-size: inherit; in the CSS while we’re at it).
  • Set <body> and <a> to currentColor) at base.
  • Write shorthand, inheriting borders (e.g. 1px solid or 1px solid currentColor).

Using this theme model, we might generate any number of themes, perhaps storing them as utility classes, or looping over a list of modifiers inside a component, or just allowing the user to set variables right on the block in the CMS. When IE 11 drops below 1% in our stats, we can do much more with variables, but this is enough for our current purposes.

Let’s not get side-tracked. What about grid?!

Content components

Grid lets us describe exactly what content we have in each partial in a new way. It’s really a game changer for design agencies building new UI for every project and we’re discovering new (and fun) applications for it as we explore.

To give context: we customize each interface for our clients, with custom fields made to suit their specific needs and their content model, using Craft CMS. We have internal tools that pull in events from ticketing APIs and create entries from that data, which may then be edited and expanded (or created entirely) in the CMS. The client can fill in or edit named fields in permanent page regions, and also add in whole designed, branded content blocks into the layout of each page as they build them.

There’s a lot of UI. The clients have a lot of control over content and we have a lot of control over the HTML, so we can ensure a high standard of accessible, semantic code on the page. We develop the content model together during discovery and then turn ’em loose on content creation. They add what they want and we ensure that it works and always looks right. Better than right! Super. (Sorry! :P)

So, as a developer, I have to balance competing priorities:

  • Accessibility, usability
  • Branding and graphic design
  • Performance
  • Maintenance and codebase health

Let’s look at those one by one:

Accessibility

Accessible, logical HTML is my jam. At minimum, I require a green accessibility score on Lighthouse score for my projects. (Who am I kidding, I want that delicious 100!) Core paths and pages are tested with a couple of screen readers, the keyboard tab, keyboard navigation), low vision simulators, dasher, voice access and binary switch. (I also work for Robots and Cake so this is a big part of my development.) I add giant clickable phone numbers and email addresses to pages over and over. I just want to get people where they are going.

I’ve been concerned about the way content can be re-ordered with grid (and flexbox, for that matter). Having gone through a few builds now, I actually think grid can help us with this problem. With CSS Grid, there’s no reason to move around HTML in service to the layout. We can go back to thinking about the whole document as a logical, linear sequence as our first concern.

Branding vs. Performance vs. Maintenance

Arts venues require high-spec graphic design, unified across print and web, and have constantly changing materials (e.g. programs, brochures, tickets, posters, microsites, etc.) they need to get out to their audiences, including contractual marketing obligations that must be met. As you can imagine, we have a lot of high quality large images we have to prioritize and typically come with strong print-led branding. That means we may be serving around fifteen custom fonts (including weight variations, display faces, etc.) and complex CSS to the page as well. We have to keep ourselves as lean as we can. We are shipping CSS that’s around 20 KB nano Gzipped at the moment but I’m working on reducing it further.

However, we do keep the grid area names full length by setting reduce identifiers to false in our PostCSS task. It’s vastly more useful to have the layout maps available in DevTools than it is to save those few bytes. For maintenance, self-documentation, and the sake of your future self who is debugging this site without repo access on a delayed train in Sowerby Bridge: keep the maps.

Code health

The way to balance all these competing needs is to articulate and agree on conventions so that there’s less to fix in testing and so that solved problems stay solved. We examine all the components we build and make sure they always start with a heading, that links go places, and buttons trigger actions, that countable objects are delivered as a list and preceded by a landmark heading, that navs are <nav> and times are <time> and div soup is eaten for breakfast— the basics.

With CSS Grid, there’s no excuse to move around HTML in service to the layout. Your content can always flow logically while changes in layout happen in CSS. And, as there’s no need for margins or padding to create gutters, you can simply declare:

.o-grid .o-grid { width:100%; }

…to be sure any number of nested groups all visually occupy the same page grid. The HTML can be a clearer guide to what things really are: a closer document.

See the Pen
lock down semantic accessible structures
by limograf (@Sally_McGrath)
on CodePen.

There’s a lot to manage between the heading and the action, and it’s my challenge to keep track of all these fields in all those components and make it traversable, scannable, linearizable, and easily read in some kind of logical, understandable manner, while making sure I’m faithfully executing the design spec.

Let’s bring in my first, surprisingly useful, grid mixin.

@mixin template($ elements...) {   @each $ element in $ elements {     &__#{$ element} {       grid-area: $ element;     }   } }

Using this mixin everywhere means:

  1. Each component partial now starts out with a list of all its possible elements, which is a very handy piece of documentation, especially when Twigging the actual front-end component.
  2. The mixin takes care of assigning the grid areas.
  3. Element and component names stay consistent across Sketch, CSS, and HTML and any inconsistencies will be very obvious, as the layout will fail. I’m firm, but fair.
  4. BEM naming is enforced automatically but isn’t muddling things up in the partial.

Now, in the partial, we will just declare grid-template-areas, using normal English words, giving us a series of maps of the layouts that also match the database fields. Super readable!

Here’s an example of this mixin working:

See the Pen
BEM ELEMENT AUTO ASSIGN
by limograf (@Sally_McGrath)
on CodePen.

We decided to stick to named areas for internal grids because I read a great article on this very site explaining how Autoprefixer can handle grid for IE 11 if you stick to the listed supported properties — and it does for the most part. If you view this test case with Autoprefixer applied in the super useful Debug Mode in a browser test, you’ll see it working.

So far, so good.

But there are pitfalls! You must set inline elements to block to make sure they always operate as grid cells in IE 11. Comment out the marked line in the example to see what happens otherwise:

Debug has caught an issue.

Ouch! Be careful with those blocks. You may find some versions of IE 11 don’t even pick up this fix, in which case you might try just using plain ol’ <p> tags… sigh.

I don’t include display: grid in this mixin because there are scenarios where the actual grid is set on an inner container, for example, but we’d still want the grid-areas to match on the correct BEM class.

So:

.c-header{    @include template(title, pretitle, posttitle, producer, venue, credit, quote, nav, infobar, search); }

Let’s lay these suckers out.

Layout

Let’s identify a few more rules of the road to ensure this component slides right into a page layout without hassle. At time of writing, there’s no subgrid) available (but there will be!), so this component knows nothing of the parent grid it’s living in. This happens to match the BEM component approach well — as each component is written flat, and orphaned, to limit inheritance. I’m not advocating for BEM (or BEM-ish as we obviously use) here — I’m just saying that if you’re already using it, this is a bonus.

In this example, the designer has set a page layout of 12 column grid with 20px (1.25rem) gutters, site-wide, with no offset pieces. Our component is a page region and will occupy all 12 grid columns. In this transitional period, we’re still using this kind of set grid as we have a ton of systems still based on this idea that we have to integrate with. So, here’s our convention for this condition: for a full width region drop the grip gap and write the grid template columns as fractional units (fr) of 12.

Doing things this way means:

  1. the sight lines of this internal grid broadly follow the grid it sits within;
  2. it’s easy to see the underlying design rules in the code; and
  3. it’s easy to line things up exactly, if required.

A quick note on “lining up”

Wait… what do I mean ‘to have things line up exactly‘? Doesn’t it already line up exactly?

Two equal columns split mid-gutter of the parent 12-column grid.

Well, no. The fractional units approach divides across the space perfectly, so you end up in the gutter. Two even columns lands you halfway across the gutter. Two columns where one is 2/3 and the other is 1/3 will split 1/3 of the way across that gutter, and so on.

Two unequal columns (set to 2fr and 1fr, respectively) split a third of the way into a gutter of the 12-column parent grid.

It’s not exactly hard to fix the alignment, as we know the width of our page grid gutter. For example, on an even split, we could include the grid gap.

However, we can’t do that with any other division. What we can do is add that gap as a margin — the margin is added inside no matter what box sizing you have set. In this example, we have three columns (two named areas and one empty space), splitting our gutter into thirds:

This is how to calculate those margins: Make sure the total fr units sum results in 12. Divide the grid gap by the number of columns in the parent grid, then multiply that like so:

The right margin multiplier of n is equal to the sum of the fr units to the right of n. The left margin of n is equal to the sum of the fr units to the left of n.

So, for grid-template-columns with a value of 2fr 3fr 2fr 4fr 1fr:

 2      3      2     4    1  0/10   2/7    5/5   7/1  11/0

See the Pen
name spec inside and number spec outside — page region, desktop
by limograf (@Sally_McGrath)
on CodePen.

You could even write that as a mixin if you find yourself writing calc() a lot. Something like this for aligning an inner grid to the parent grid:

See the Pen
auto align inner grid to parent grid
by limograf (@Sally_McGrath)
on CodePen.

…and something like this to auto-calculate margins when the name is specified inside but the number is specified outside the grid:

See the Pen
name spec inside and number spec outside — auto calc margins
by limograf (@Sally_McGrath)
on CodePen.

I’m sure you can think of other solutions, like switching to named lines, or adding in extra fixed-width columns or even writing all maps with 12 named areas per row. There are so many ways you can deal with this, but I think a lot of them remove the advantage of named areas. Areas give us a readable layout map that contains what our future selves need to know. It is code as documentation.

To be clear, the design problem I’m walking us through is not one of alignment. Alignment is easy with grid. The question is not of solving the immediate, trivial, layout problem, but of solving it in a way that supports our goal of being able to come back in six months and grasp:

  1. What elements are in the component.
  2. How they are laid out.
  3. Why the code is written in this way.

The grid specification is huge and it’s easy to get lost in the options. Perhaps it’s a better plan to reset to a 12-column grid and use the number spec (i.e. explicitly link to our page grid, which uses the number spec) when absolute alignment is required — but I do feel there’s a smarter, simpler solution waiting to be found. For this site, we ended up writing a page grid object and added nested internal grid cells to it with classes: .o-page-grid__sidebar.

What do you all think? I definitely foresee differing perspectives on this. 🤦‍♀️

A real, live grid!

We can use this to create a generic page header:

See the Pen
01 – Generic Page header
by limograf (@Sally_McGrath)
on CodePen.

Or, we can create a variation of the homepage:

See the Pen
02 – Home page
by limograf (@Sally_McGrath)
on CodePen.

What about a hero header that breaks out of our container? Sure! Or we can deliver it outside the container as well:

See the Pen
03 – Hero
by limograf (@Sally_McGrath)
on CodePen.

What next? A themed event header with a full width info bar that sticks and an internal button that lines up with the sidebar on the parent grid? You bet. I’ll include a parent grid so it’s easier to see:

See the Pen
04 – Event header
by limograf (@Sally_McGrath)
on CodePen.

What about a search with a central alignment? Let’s use a collapsing columns technique:

See the Pen
06 – Search with central alignment
by limograf (@Sally_McGrath)
on CodePen.

Here’s a demo of that one as a partial. Yes, it’s a map! And it’s a wrap!

Conventions

Phew, we covered a lot! But you can see how flexible and self-documenting a system like this can be, right?

  1. Type is handled with a separate type system.
  2. Colors are handled by a theme partial that describes the underlying color rules of the design, rather than simply coloring elements ad hoc.
  3. Elements are called what they are, in English, and included as a list at the top of the partial with the template mixin. This list can be taken into Twig or a template as a reference.
  4. Correct HTML is always used and nesting doesn’t break grid. That means you can apply any number of nested grids to the same layout space by setting a convention.
  5. Precise alignment is done in a number spec, and not a name spec (but note that alignment is possible with name spec).
  6. IE 11 is supported.
  7. I do have one more quick note and example of another component built with named areas. In this example, cards are not regions, but components placed in a grid, so there’s no reason to use the fr of 12 convention. You can expect a media object partial to look like this:

    .c-card {   &--news {     align-content: start;     grid-template-areas:        "image"       "datetime"       "title";   }    &--search {     justify-content: start;     grid-template-columns: 1fr 3fr;     grid-template-areas:       "image page"       "image title"       "image summary";   }    &--merchandise {     grid-gap: 0;     grid-template-columns: $ b 1fr 1fr $ b;     grid-template-areas:       "image image   image   image"       ".     title   title   ."       ".     summary summary ."       ".     price   action  .";   }    &--donations {     // donations thanks button is too long and must take up more space than input     grid-gap: 0;     grid-template-columns: $ b 1fr 2fr $ b;     grid-template-areas:       "image image   image   image"       ".     title   title   ."       ".     summary summary ."       ".     input   action  .";   } }  // ...

    The post Code as Documentation: New Strategies with CSS Grid appeared first on CSS-Tricks.

    CSS-Tricks

, , ,
[Top]