{"id":263178,"date":"2017-12-01T13:09:58","date_gmt":"2017-12-01T20:09:58","guid":{"rendered":"http:\/\/css-tricks.com\/?p=263178"},"modified":"2017-12-01T13:09:58","modified_gmt":"2017-12-01T20:09:58","slug":"roman-empire-made-pure-css-connect-4-possible","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/roman-empire-made-pure-css-connect-4-possible\/","title":{"rendered":"How the Roman Empire Made Pure CSS Connect 4 Possible"},"content":{"rendered":"

Experiments are a fun excuse to learn the latest tricks, think of new ideas, and push your limits. “Pure CSS” demos have been a thing for a while, but new opportunities open up as browsers and CSS itself evolves. CSS and HTML preprocessors also helped the scene move forward. Sometimes preprocessors are used for hardcoding every possible scenario, for example, long strings of :checked<\/code> and adjacent sibling selectors. <\/p>\n

In this article, I will walk through the key ideas of a Pure CSS Connect 4 game I built. I tried to avoid hardcoding as much as I could in my experiment and worked without preprocessors to focus on keeping the resulting code short. You can see all the code and the game right here:<\/p>\n

See the Pen Pure CSS Connect 4<\/a> by Bence Szab\u00f3 (@finnhvman<\/a>) on CodePen<\/a>.<\/p>\n

Essential concepts<\/h3>\n

I think there are some concepts that are considered essential in the “pure CSS” genre. Typically form elements are used for managing state and capturing user actions. I was excited when I found people use <button type=\"reset\"><\/code> to reset or start a new game. All you have to do is wrap your elements in a <form><\/code> tag and add the button. In my opinion this is a much cleaner solution than having to refresh the page. <\/p>\n

My first step was to create a form element then throw a bunch of inputs into it for the slots and add the reset button. Here is a very basic demonstration of <button type=\"reset\"><\/code> in action:<\/p>\n

See the Pen Pure HTML Form Reset<\/a> by Bence Szab\u00f3 (@finnhvman<\/a>) on CodePen<\/a>.<\/p>\n

I wanted to have nice visual for this demo to provide a full experience. Instead of pulling in an external image for the board or the discs, I used a radial-gradient()<\/code>. A nice resource I often use is Lea Verou’s CSS3 Patterns Ga<\/a>l<\/a>lery<\/a>. It is a collection of patterns made by gradients, and they’re editable too! I used currentcolor<\/a><\/code>, which came pretty handy for the disc pattern. I added a header and reused my Pure CSS Ripple Button<\/a>.<\/p>\n

\"\"
At this point the layout and disc design was already final, only the game didn’t work at all<\/figcaption><\/figure>\n

Dropping discs onto the board<\/h3>\n

Next I enabled users to take their turns dropping discs onto the Connect 4 board. In Connect 4, players (one red and one yellow) drop discs into columns in alternating turns. There are 7 columns and 6 rows (42 slots). Each slot can be empty or occupied by a red or yellow disc. So, a slot can have three states (empty, red, or yellow). Discs dropped in the same column are stacked onto each other.<\/p>\n

I started out by placing two checkboxes for each slot. When they’re both unchecked the slot is considered empty, and when one of them is checked the corresponding player has its disc in it. <\/p>\n

The possible state of having them both checked should be avoided by hiding them once either of them is checked. These checkboxes are immediate siblings, so when the first of a pair is checked you can hide both by using :checked<\/code> pseudo-class and the adjacent sibling combinator (+<\/code>). What if the second is checked? You can hide the second one, but how to affect the first one? Well, there is no previous sibling selector, that’s just not how CSS selectors work. I had to reject this idea.<\/p>\n

Actually, a checkbox can have three<\/em> states by itself, it can be in the indeterminate<\/code> state<\/a>. The problem is that you can’t put it into indeterminate state with HTML alone. Even if you could, the next click on the checkbox would always make it transform into checked state. Forcing the second player to double-click when they make their move is unreliable and unacceptable.<\/p>\n

I was stuck on the MDN doc of :indeterminate<\/code><\/a> and noticed that radio inputs also have indeterminate state. Radio buttons with the same name are in this state when they’re all unchecked. Wow, that’s an actual initial state! What’s really beneficial is that checking the latter sibling also has an effect on the former one! Thus I filled the board with 42 pairs of radio inputs.<\/p>\n

In retrospect, clever ordering and usage of labels with either checkboxes or radio buttons would have made the trick, but I didn’t consider labels to be an option to keep the code simpler and shorter.<\/p>\n

I wanted to have large areas for interaction to have nice UX, so I thought it’s reasonable to let players make a move by clicking on a column. I stacked controls of the same column on each other by adding absolute and relative positioning to the appropriate elements. This way only the lowest empty slot could be selected within a column. I meticulously set the time of transition of disc fall per row and their timing function is approximating a quadratic curve to resemble realistic free fall. So far the pieces of the puzzle came well together, though the animation below clearly shows that only the red player could make their moves.<\/p>\n

\"\"
Even though all the controls are there, only red discs can be dropped on the board<\/figcaption><\/figure>\n

The clickable areas of radio inputs are visualized with colored but semi-transparent rectangles. The yellow and red inputs are stacked over each other six times(=six rows) per column, leaving the red input of the lowest row on top of the stack. The mixture of red and yellow creates the orangish color which can be seen on the board at start. The less empty slots are available in a column, the less intense this orangish color gets since the radio inputs are not displayed once they are not :indeterminate<\/code>. Due to the red input always being precisely over the yellow input in every slot, only the red player is able to make moves.<\/p>\n

Tracking turns<\/h3>\n

I only had a faint idea and a lot of hope that I can somehow solve switching turns between the two players with the general sibling selector. The concept was to let the red player take turn when the number of checked inputs was even (0, 2, 4, etc.) and let the yellow player take turn when that number was odd. Soon I realized that the general sibling selector does not (and should not!) work the way I wanted.<\/p>\n

Then a very obvious choice was to experiment with the nth selectors. However attracting it was to use the even<\/code> and odd<\/code> keywords, I ran into a dead end. The :nth-child selector<\/a> “counts” the children within a parent, regardless of type, class, pseudo-class, whatever. The :nth-of-type selector<\/a> “counts” children of a type within a parent, regardless of class or pseudo-class. So the problem is that they cannot count based on the :checked<\/a><\/code> state. <\/p>\n

Well CSS counters<\/a> count too, so why not give them a try? A common usage of counters is to number headings (even in multiple levels) in a document. They are controlled by CSS rules, can be arbitrarily reset at any point and their increment (or decrement!) values can be any integer. The counters are displayed by the counter()<\/code> function in the content<\/a><\/code> property.<\/p>\n

The easiest step was to set up a counter and count the :checked<\/code> inputs in the Connect 4 grid. There are only two difficulties with this approach. The first is you cannot perform arithmetics on a counter to detect if its is even or odd. The second is that you cannot apply CSS rules to elements based on the counter value.<\/p>\n

I managed to overcome the first issue by making the counter binary. The value of the counter is initially zero. When the red player checks their radio button the counter is incremented by one. When the yellow player checks their radio button the counter is decremented by one, and so on. Therefore the counter value will be either zero or one, even or odd.<\/p>\n

Solving the second problem required much more creativity (read: hack). As mentioned counters can be displayed, but only in the ::before<\/code> and ::after<\/code><\/a> pseudo-elements. That is a no-brainer, but how can they affect other elements? At the very least the counter value can change the width of the pseudo-element. Different numbers have different widths. Character 1<\/code> is typically thinner than 0<\/code>, but that is something very hard to control. If the number of characters change rather than the character itself the resulting width change is more controllable. It is not uncommon to use Roman numerals with CSS counters. One and two represented in Roman numerals are the same character once and twice and so are their widths in pixels.<\/p>\n

My idea was to attach the radio buttons of one player (yellow) to the left, and attach the radio buttons of the other player (red) to the right of their shared parent container. Initially, the red buttons are overlaid on the yellow buttons, then the width change of the container would cause the red buttons to “go away” and reveal the yellow buttons. A similar real-world concept is the sliding window with two panes, one pane is fixed (yellow buttons), the other is slidable (red buttons) over the other. The difference is that in the game only half of the window is visible.<\/p>\n

So far, so good, but I still wasn’t satisfied with font-size<\/code> (and the other font<\/code> properties) indirectly controlling the width. I thought letter-spacing<\/code> would fit nicely here, since it only increases the size in one dimension. Unexpectedly, even one letter has letter spacing (which is rendered after the letter), and two letters render the letter spacing twice. Predictable widths are crucial to make this reliable. Zero width characters along with single and double letter spacing would work, but it is dangerous to set the font-size<\/code> to zero. Defining large letter-spacing<\/code> (in pixels) and tiny (1px<\/code>) font-size<\/code> made it almost consistent across all browsers, yes I’m talking about sub-pixels.<\/p>\n

I needed the container width to alternate between initial size (=w<\/code>) and at least double the initial size (>=2w<\/code>) to be able to fully hide and show the yellow buttons. Let’s say v<\/code> is the rendered width of the ‘i’ character (lower roman representation, varies across browsers), and c<\/code> is the rendered width (constant) of the letter-spacing<\/code>. I needed v + c = w<\/code> to be true but it couldn’t be, because c<\/code> and w<\/code> are integers but v<\/code> is non-integer. I ended up using min-width<\/code> and max-width<\/code> properties to constrain the possible width values, so I also changed the possible counter values to ‘i’ and ‘iii’ to make sure the text widths underflow and overflow the constraints. In equations this looked like v + c < w<\/code>, 3v + 3c > 2w<\/code>, and v << c<\/code>, which gives 2\/3w < c < w<\/code>. The conclusion is that the letter-spacing<\/code> has to be somewhat smaller than the initial width.<\/p>\n

I have been reasoning so far as if the pseudo element displaying the counter value was the parent of the radio buttons, it is not. However, I noticed that the width of the pseudo-element changes the width of its parent element, and in this case the parent is the container of the radio buttons.<\/p>\n

If you are thinking couldn’t this be solved with Arabic numerals? You are right, alternating the counter value between something like ‘1’ and ‘111’ would also work. Nevertheless, Roman numerals gave me the idea in the first place, and they were also a good excuse for the clickbaity title so I kept them.<\/p>\n

\"\"
The players take alternating turns starting with the red player<\/figcaption><\/figure>\n

Applying the technique discussed makes the parent container of the radio inputs double in width when a red input is checked and makes it original width when a yellow input is checked. In the original width container the red inputs are over the yellow ones, but in the double width container, the red inputs are moved away.<\/p>\n

Recognizing patterns<\/h3>\n

In real life, the Connect 4 board does not tell you if you have won or lost, but providing proper feedback is part of good user experience in any software. The next objective is to detect whether a player has won the game. To win the game a player has to have four of their discs in a column, row or diagonal line. This is a very simple task to solve in many programming languages, but in pure CSS world, this is a huge challenge. Breaking it down to subtasks is the way to approach this systematically.<\/p>\n

I used a flex container as the parent of the radio buttons and discs. A yellow radio button, a red radio button and a div for the disc belong to a slot. Such a slot is repeated 42 times and arranged in columns that wrap. Consequently, the slots in a column are adjacent, which makes recognizing four in a column the easiest part using the adjacent selector:<\/p>\n

<div class=\"grid\">\r\n  <input type=\"radio\" name=\"slot11\">\r\n  <input type=\"radio\" name=\"slot11\">\r\n  <div class=\"disc\"><\/div>\r\n  <input type=\"radio\" name=\"slot12\">\r\n  <input type=\"radio\" name=\"slot12\">\r\n  <div class=\"disc\"><\/div>\r\n  ...\r\n  <input type=\"radio\" name=\"slot16\">\r\n  <input type=\"radio\" name=\"slot16\">\r\n  <div class=\"disc\"><\/div>\r\n\r\n  <input type=\"radio\" name=\"slot21\">\r\n  <input type=\"radio\" name=\"slot21\">\r\n  <div class=\"disc\"><\/div>\r\n  ...\r\n<\/div><\/code><\/pre>\n
\/* Red four in a column selector *\/\r\ninput:checked + .disc + input + input:checked + .disc + input + input:checked + .disc + input + input:checked ~ .outcome\r\n\r\n\/* Yellow four in a column selector *\/\r\ninput:checked + input + .disc + input:checked + input + .disc + input:checked + input + .disc + input:checked ~ .outcome<\/code><\/pre>\n

This is a simple but ugly solution. There are 11 type and class selectors chained together per player to cover the case of four in a column. Adding a div<\/code> with class of .outcome<\/code> after the elements of the slots makes it possible to conditionally display the outcome message. There is also a problem with falsely detecting four in a column where the column is wrapped, but let’s just put this issue aside.<\/p>\n

A similar approach for detecting four in a row would be truly a terrible idea. There would be 56 selectors chained together per player (if I did the math right), not to mention that they would have a similar flaw of false detection. This is a situation where the :nth-child(An+B [of S])<\/a> or the column combinators<\/a> will come handy in the future.<\/p>\n

For better semantics one could add a new div<\/code> for each column and arrange the slot elements in them. This modification would also eliminate the possibility of false detection mentioned above. Then detecting four in a row could go like: select a column where the first red radio input is checked, and select the adjacent sibling column where the first red radio input is checked, and so on two more times. This sounds very cumbersome and would require the “parent” selector<\/a>.<\/p>\n

Selecting the parent is not feasible, but selecting the child is. How would detecting four in a row go with available combinators and selectors? Select a column, then select its first red radio input if checked, and select the adjacent column, then select its first red radio input if checked, and so on two more times. It still sounds cumbersome, yet possible. The trick is not only in the CSS but also in the HTML, the next column has to be the sibling of the radio buttons in the previous column creating a nested structure.<\/p>\n

<div class=\"grid column\">\r\n  <input type=\"radio\" name=\"slot11\">\r\n  <input type=\"radio\" name=\"slot11\">\r\n  <div class=\"disc\"><\/div>\r\n  ...\r\n  <input type=\"radio\" name=\"slot16\">\r\n  <input type=\"radio\" name=\"slot16\">\r\n  <div class=\"disc\"><\/div>\r\n\r\n  <div class=\"column\">\r\n    <input type=\"radio\" name=\"slot21\">\r\n    <input type=\"radio\" name=\"slot21\">\r\n    <div class=\"disc\"><\/div>\r\n    ...\r\n    <input type=\"radio\" name=\"slot26\">\r\n    <input type=\"radio\" name=\"slot26\">\r\n    <div class=\"disc\"><\/div>\r\n\r\n    <div class=\"column\">\r\n      ...\r\n    <\/div>\r\n  <\/div>\r\n<\/div><\/code><\/pre>\n
\/* Red four in a row selectors *\/\r\ninput:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column::after,\r\ninput:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column::after,\r\n...\r\ninput:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column::after<\/code><\/pre>\n

Well the semantics are messed up and these selectors are only for the red player (another round goes for the yellow player), on the other hand it does work. A little benefit is that there will be no falsely detected columns or rows. The display mechanism of the outcome also had to be modified, using the ::after<\/code> pseudo element of any matching column is a consistent solution when proper styling is applied. As a result of this, a fake eighth column has to be added after the last slot.<\/p>\n

As seen in the code snippet above, specific positions within a column are matched to detect four in a row. The very same technique can be used for detecting four in a diagonal by adjusting these positions. Note that the diagonals can are in two directions.<\/p>\n

input:nth-of-type(2):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column::after,\r\ninput:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(10):checked ~ .column::after,\r\n...\r\ninput:nth-of-type(12):checked ~ .column > input:nth-of-type(10):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(6):checked ~ .column::after<\/code><\/pre>\n

The number of selectors have increased vastly in the final run, and this is definitely a place where CSS preprocessors could reduce the length of the declaration. Still, I think the demo is moderately short. It should be somewhere around the middle on the scale from hardcoding a selector for every possible winning pattern to using 4 magical selectors (column, row, two diagonals).<\/p>\n

\"\"
A message is shown when a player wins<\/figcaption><\/figure>\n

Closing loopholes<\/h3>\n

Any software has edge cases and they need to be handled. The possible outcomes of a Connect 4 game are not only the red, or yellow player winning, but neither player winning filling the board known as draw. Technically this case doesn’t break the game or produce any errors, what’s missing is the feedback to the players.<\/p>\n

The goal is to detect when there are 42 :checked<\/code> radio buttons on the board. This also means that none of them are in the :indeterminate<\/code> state. That is requiring<\/a> a selection to be made for each radio group. Radio buttons are invalid, when they are :indeterminate<\/code>, otherwise they are valid. So I added the required<\/code> attribute for each input, then used the :valid<\/code> pseudo-class on the form to detect draw.<\/p>\n

\"\"
The draw outcome message is shown when the board is filled<\/figcaption><\/figure>\n

Covering the draw outcome introduced a bug. In the very rare case of the yellow player winning on last turn, both the win and draw messages are displayed. This is because the detection and display method of these outcomes are orthogonal. I worked around the issue by making sure that the win message has a white background and is over the draw message. I also had to delay the fade in transition of the draw message, so it would not get blended with the win message transition.<\/p>\n

\"\"
The yellow wins message is over the draw outcome preventing it to be displayed<\/figcaption><\/figure>\n

While a lot of radio buttons are hid behind each other by absolute positioning, all of those in indeterminate state can still be accessed by tabbing through the controls. This enables players to drop theirs discs into arbitrary slots. A way to handle this is to simply forbid keyboard interactions by the tabindex<\/code> attribute: setting it to -1<\/code> means that it should not be reachable via sequential keyboard navigation. I had to augment every radio input with this attribute to eliminate this loophole.<\/p>\n

<input type=\"radio\" name=\"slot11\" tabindex=\"-1\" required>\r\n<input type=\"radio\" name=\"slot11\" tabindex=\"-1\" required>\r\n<div class=\"disc\"><\/div>\r\n...<\/code><\/pre>\n

Limitations<\/h3>\n

The most substantial drawback is that the board isn’t responsive and it might malfunction on small viewports due to the unreliable solution of tracking turns. I didn’t dare to take the risk of refactoring to a responsive solution, due to the nature of the implementation it feels much safer with hardcoded dimensions.<\/p>\n

Another issue is the sticky hover on touch devices. Adding some interaction media queries<\/a> to the right places is the easiest way to cure this, though it would eliminate the free fall animation.<\/p>\n

One might think that the :indeterminate<\/code> pseudo-class is already widely supported, and it is. The problem is that it is only partially supported in some browsers. Observe Note 1 in the compatibility table<\/a>: MS IE and Edge do not support it on radio buttons. If you view the demo in those browsers your cursor will turn into the not-allowed<\/code> cursor on the board, this is an unintentional but somewhat graceful degradation.<\/p>\n

\"\"
Not all browsers support :indeterminate on radio buttons<\/figcaption><\/figure>\n

Conclusion<\/h3>\n

Thanks for making it to the last section! Let’s see some numbers:<\/p>\n

    \n
  • 140 HTML elements<\/li>\n
  • 350 (reasonable) lines of CSS<\/li>\n
  • 0 JavaScript<\/li>\n
  • 0 external resources<\/li>\n<\/ul>\n

    Overall, I’m satisfied with the result and the feedback was great. I sure learned a lot making this demo and I hope I could share a lot writing this article!<\/p>\n","protected":false},"excerpt":{"rendered":"

    Experiments are a fun excuse to learn the latest tricks, think of new ideas, and push your limits. “Pure CSS” demos have been a thing for a while, but new opportunities open up as browsers and CSS itself evolves. CSS and HTML preprocessors also helped the scene move forward. Sometimes preprocessors are used for hardcoding […]<\/p>\n","protected":false},"author":250779,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4],"tags":[],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":321850,"url":"https:\/\/css-tricks.com\/how-to-write-loops-with-preprocessors\/","url_meta":{"origin":263178,"position":0},"title":"How to Write Loops with Preprocessors","date":"November 4, 2020","format":false,"excerpt":"Loops are one of those features that you don't need every day. But when you do, it's awfully nice that preprocessors can do it because native HTML and CSS cannot. Sass (SCSS) for Loop CodePen Embed Fallback while Loop CodePen Embed Fallback each Loop CodePen Embed Fallback Less for Loop\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/11\/loops.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":145590,"url":"https:\/\/css-tricks.com\/the-other-interface\/","url_meta":{"origin":263178,"position":1},"title":"The “Other” Interface","date":"August 3, 2013","format":false,"excerpt":"I like this sentiment by Robin Rendle: As front-end developers and designers, we\u2019re constantly refining two interfaces simultaneously: one for visitors who load the website, the other for developers who have to tackle the code in the future Surprise! Preprocessors give you the organizational tools to do a good job\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":312524,"url":"https:\/\/css-tricks.com\/css-is-and-where-are-coming-to-browsers\/","url_meta":{"origin":263178,"position":2},"title":"CSS :is() and :where() are coming to browsers","date":"June 10, 2020","format":false,"excerpt":"\u0160ime Vidas with the lowdown on what these pseudo-selectors are and why they will be useful: :is() is to reduce repetition\u00b9 of parts of comma-separated selectors. :where() is the same, but nothing inside it affects specificity. The example of wrapping :where(:not()) is really great, as now there is a way\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/06\/is-where-pseudo.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":158614,"url":"https:\/\/css-tricks.com\/abstraction-in-web-languages-and-variables-and-stuff\/","url_meta":{"origin":263178,"position":3},"title":"About Variables in CSS and Abstractions in Web Languages","date":"December 18, 2013","format":false,"excerpt":"Variables are coming to CSS. They already have implementations, so there is no stopping it now. Firefox has them in version 29 and Chrome has them unprefixed in 29+ if you have the \"Enable experimental Web Platform features\" flag turned on. To be clear, no amount of arguing on whether\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":352283,"url":"https:\/\/css-tricks.com\/imba\/","url_meta":{"origin":263178,"position":4},"title":"imba","date":"September 20, 2021","format":false,"excerpt":"It's not every day you see a new processor for building websites that reinvents the syntax for HTML and CSS and JavaScript. That's what imba is doing. That's an awful lot of vendor lock-in, but I guess if you get over the learning curve and it helps you build performant\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/09\/imba.png?fit=1200%2C611&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":191869,"url":"https:\/\/css-tricks.com\/approaches-media-queries-sass\/","url_meta":{"origin":263178,"position":5},"title":"Approaches to Media Queries in Sass","date":"December 30, 2014","format":false,"excerpt":"Using media queries in CSS as part of responsive websites is bread and butter stuff to todays front-end developer. Using preprocessors to make them more comfortable to write and easier to maintain has become common practice as well. I spent a few months experimenting with a dozen different approaches to\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"featured_media_src_url":null,"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/263178"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/250779"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=263178"}],"version-history":[{"count":7,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/263178\/revisions"}],"predecessor-version":[{"id":263211,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/263178\/revisions\/263211"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=263178"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=263178"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=263178"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}