74 | 75 |
About
79 |What is DoCSSa ?
84 | 85 |DoCSSa is a CSS architecture and methodology to help structure rapid and maintainable
86 | stylesheets development.
87 | It is intended for use in large, long lived sites, on which many frontend developers may be working
88 | over time.
89 | The name stands for Deferred Object CSS Architecture.
What are the key features of DoCSSa ?
92 | 93 |-
94 |
- 95 | 96 | Clear and proven folder structure 97 | 98 | 99 |
- 100 | 101 | Separation of concerns between HTML and CSS 102 | 103 | 104 |
- 105 | 106 | Scalable and maintainable stylesheets 107 | 108 | 109 |
- 110 | 111 | Easy to setup and flexible 112 | 113 | 114 |
Why does DoCSSa exist ?
117 | 118 |As frontend web developers, we are living in a world of evergrowing complexity. What once was a 119 | relatively easy thing to do (putting aside browsers inconsistencies) has grown into a very complex, 120 | dynamic, everchanging reality.
121 | 122 |So, we need flexible structures in order to be more efficient and less 123 | under pressure when the rough times come. The more work we 124 | can avoid by having defined the right tools and the right architecture ahead of time, the better.
125 | 126 |DoCSSa is our attempt at an organization that aims toward a fast workflow that will keep the 127 | flexibility required for the long run.
128 | 129 |How does DoCSSa bring its benefits ?
130 | 131 |DoCSSa is based on Sass, and leverages some of its key features to achieve its goal. 132 | Most notably, DoCSSa takes advantage of imports and mixins. 133 | It also suggests a file system organisation and a BEM based naming convention, and integrates 134 | the core ideas found in OOCSS and SmaCSS.
135 | 136 |141 | 142 |
File Structure
146 |Basics
151 | 152 |In DoCSSa, the file system is divided in four main directories in the sass folder :
153 | 154 |-
155 |
-
156 | 157 | base 158 |159 |
160 |
-
161 | 162 | components 163 |164 |
165 |
-
166 | 167 | specifics 168 |169 |
170 |
-
171 | 172 | vendor 173 |174 |
175 |
Each base folder has a specific role, and CSS rules for a particular set of elements will end up in one of
178 | these directories, according to their nature.
179 | The structure has a single point of entry, which we'll call custom.scss.
180 | This file is located at the root of the sass folder.
In most cases, it will be the only file with no
183 | underscore(_) prefix: as you may know, the .scss to .css convertion process in Sass only converts
184 | files that don't have an underscore in front of them. Most of the files in our structure being imported
185 | by other files, they don't need to be rendered directly.
186 | Only components may be rendered separately, in order to be able to dynamically load them if needed,
187 | but we'll get back to it.
188 |
The main .scss file will look something like this :
191 | 192 |-
194 |
- 195 | // custom.scss 196 | 197 |
200 | @charset "UTF-8"; 201 | 202 | /*! 203 | ========== INIT 204 | */ 205 | // styles reset (normalize) and third party scss entry point 206 | @import 'vendor/__vendor'; 207 | 208 | 209 | /*! 210 | ========== BASE 211 | */ 212 | // variables, fonts, mixins, helpers... common styles used across the entire site 213 | @import 'base/__base'; 214 | 215 | 216 | /*! 217 | ========== COMPONENTS 218 | */ 219 | // reusable components 220 | @import 'components/__components'; 221 | 222 | 223 | /*! 224 | ========== SPECIFICS 225 | */ 226 | // declarations specific to the project, organized according to the items/sections represented 227 | @import 'specifics/__specifics'; 228 |229 |
As you can see, it mostly acts as an aggregator for other files, which themselves import some 232 | other scss files, and so on. With a well thought organization, this simple construct can prove very powerful!
233 | 234 |On a sidenote, remember that Sass is only a CSS precompiler, so you'll end up with only one .css file 235 | with all the goodies, not a bunch of HTTP requests.
236 | 237 |Here is an overview of what a basic file system might look like :
238 | 239 | // file system 240 |241 | sass 242 | ¦ custom.scss 243 | ¦ 244 | +---base 245 | ¦ ¦ __base.scss 246 | ¦ ¦ _config.scss 247 | ¦ ¦ 248 | ¦ +---project 249 | ¦ ¦ __project.scss 250 | ¦ ¦ _fonts.scss 251 | ¦ ¦ _globals.scss 252 | ¦ ¦ _mixins.scss 253 | ¦ ¦ _variables.scss 254 | ¦ ¦ 255 | ¦ +---utils 256 | ¦ __utils.scss 257 | ¦ _mixins.scss 258 | ¦ _system.scss 259 | ¦ 260 | +---components 261 | ¦ ¦ __components.scss 262 | ¦ ¦ 263 | ¦ +---button 264 | ¦ ¦ _button.scss 265 | ¦ ¦ 266 | ¦ +---tabs 267 | ¦ _tabs.scss 268 | ¦ 269 | ¦ 270 | +---specifics 271 | ¦ ¦ __specifics.scss 272 | ¦ ¦ _main.scss 273 | | | _inbox.scss 274 | ¦ ¦ 275 | ¦ +---popins 276 | ¦ __popins.scss 277 | ¦ _popin-congratulations.scss 278 | ¦ _popin-loginForm.scss 279 | ¦ 280 | +---vendor 281 | __vendor.scss 282 |283 | 284 | 285 |
Base folder
286 | 287 |The "base" folder contains rules that are global to the site. It is divided in two parts :
288 | 300 |Note that the base/_config.scss file is located directly in the base 301 | folder. It contains variables that are required by both the utils and the project. So far 302 | only the $baseFontSize fell in that category, but we still dedicated a file to it in 303 | order to be consistent.
304 |305 | Utils contains only things that don't need to change 306 | from a project to another. As of today, it consists of _system and _mixins. 307 |
308 |-
309 |
-
310 | 311 |318 |
_system is a special file that contains tools used by DoCSSa.
316 | 317 |
312 | Most notably, it contains a mixin that helps requiring content 313 | from several places without generating duplicate content, and one that helps 314 | the provided components to make use of Modernizr classes. 315 |
319 | -
320 | 321 |373 |
_mixins contains a collection of commonly used helpers. 322 | They are particularily useful for handling vendor prefixes and 323 | fallbacks. For example, DoCSSa comes with a linear-gradient mixin that compiles to 324 | all required vendor prefixes (and only those required!) and generates a fallback 325 | color from the input values for browsers that don't support linear-gradient at all. 326 | DoCSSa recommends using Modernizr for feature detection and progressive enhancement, and the provided 327 | mixins implementing CSS3 features rely on it by default for their output (even though it can be 328 | disabled at include time).
330 | 331 |
329 |Mixins can accumulate and enrich your library 332 | with no cost, as they are only compiled when used.
339 | 340 | 372 |
333 | Mixins are a great way to avoid repetition when coding. They end up in code 334 | repetition in the css output, and for that reason the previous version of DoCSSa 335 | was based on placeholders instead, but it has now been proven that this repetition 336 | is not an issue once gzip comes into play 337 | (more 338 | info)
374 |
378 | Project is where every global setup of a project 379 | is stored. Whereas utils is intended to be kept and grow from a project to the next, 380 | project only stands true for a given project (although you may want to use it as a 381 | boilerplate from project to project). As of today, it consists of _variables, 382 | _fonts, _mixins and _globals. 383 |
384 | 385 |-
386 |
-
387 | 388 |428 |
_variables is were all site wide variables reside. Default values, 389 | color theme, configuration variables, etc. go into this file. 390 |
391 | 392 |Here's what the _variables.scss file might look like :
393 | 394 | // _variables.scss 395 | 396 |397 | /* _____ VARIABLES _____ */ 398 | 399 | // Generic 400 | // ========================================================================== 401 | 402 | $default-borderRadius: 4; 403 | $containerWidth: 760; 404 | 405 | 406 | 407 | // Colors 408 | // ========================================================================== 409 | 410 | $color-default-light: #fff; 411 | $color-default-dark: #333; 412 | $color-default-darker: #000; 413 | 414 | $color-main: #ff7f50; 415 | $color-main-light: #ff9e7e; 416 | $color-main-lighter: #ffdec7; 417 | 418 | $color-secondary: #5cbcaa; 419 | 420 | $color-alt: #f1ede4; 421 | $color-alt-dark: #c2c0bc; 422 | $color-alt-light: #f8f5ec; 423 | 424 | $color-alt2: #637c84; 425 |
426 | 427 |
429 |
430 | -
431 | 432 |446 |
The _fonts file is used —you guessed it— for the font-families declaration.
435 | 436 |
433 | In our implementation, we use a font mixin that is in charge of generating a bulletproof syntax 434 | according to the passed configuration for each needed font, according to a file naming convention.But where do we have to place the font files themselves, you may be wondering? Well, as 437 | this Sass structure is 438 | intended to be compiled to CSS in a different directory, the fonts will be in that directory. 439 | Typically, you'll have the custom.scss file in a "sass" folder" compiled to a 440 | custom.css in a 441 | "css" or "styles" folder. The font files will have to be there (preferably in a "fonts" 442 | subfolder in order to stay nice and tidy).
445 |
443 | Same goes for all the image files you may be referring to in your stylesheets. 444 |
447 |
448 | -
449 | 450 |455 |
_mixins is a place for you to store simple helper that you may want 451 | to reutilize throughout the site, but are not generic enough to endup in the 452 | utils/_mixins file. 453 |
454 |
456 |
457 | -
458 | 459 |465 |
_globals contains rules that are global 460 | to the site. Things like box-sizing type, html font size, body background color, 461 | headings and link defaults, etc. are defined here. It is also be a good place to store 462 | your layout definition if it is used for all pages on the site. 463 |
464 |
466 |
467 |
Components folder
470 | 471 |
472 | The "components" folder is where your ui-components are located. It is the place dedicated to
473 | store your reusable UI appearance.
474 | Components are an important part of DoCSSa, and we dedicated a whole section to explain all there is to know about them,
475 | so we won't say much more about them here.
476 |
Specifics folder
479 | 480 |The "specifics" folder is your common playground. It is where you'll place the rules that don't belong 481 | in the "base" or "components" folders. Until you become fluent with DoCSSa's organization system, what will 482 | eventually end up in a component will probably exist in here first.
483 | 484 |Specifics is the closest thing to your usual CSS file, except that everything in there 485 | is split and dispatched in files and folders according to what they apply to. This fragmentation 486 | is key to keeping things maintainable!
487 | 488 |By convention, we're using two underscores(__) as a prefix for files that act as an import 489 | summary, and only one underscore(_) for files which are content only. This usually evolves with the 490 | project: you begin with an underscore prefixed file, you add another one, and at some point you stop calling them 491 | directly from the main .scss file and you reorganize them in a folder with its own summary file.
492 | 511 | 512 |DoCSSa encourages you to keep your definitions tidy and reorganize in subfolders as soon as it makes
513 | sense.
514 | No file should ever be big enough that you can't scroll through it in a few mousewheel movements
515 | max!
Before everyone in your team is familiar with DoCSSa, it can be helpful for occasional contributors
518 | wondering where to place their code to have a dedicated file. In such case, we recommend using
519 | a _inbox.scss file in the "specifics" folder and ask them to commit their work in there.
520 | It shall be emptied regularly by a more experienced DoCSSa user, who would move the CSS rules
521 | defined in there to the correct place in the architecture.
522 |
Vendor folder
526 | 527 |The vendor folder is where we import SCSS files that come from third parties and can be updated at 528 | any time. It can import from a bower folder, or have its assets directly in the folder. 529 | As the .scss syntax is CSS compatible, all we have to do is to rename the .css files to 530 | .scss, in order for them to be integrated to the .css compilation instead of referenced by the CSS 531 | file as a classic @import.
532 | 533 |_normalize.scss is an excellent candidate for this section, along with more project specific 534 | third party CSS. We currently rely on bower for fetching it for us.
535 |540 | 541 |
Components
545 |Introduction
549 |Components are an important part of DoCSSa. They have been redesigned since version 1 of DoCSSa 550 | to become simpler and easier to use. They are the "Deferred Object" part of DoCCSa. 551 |
552 |We say the Object is Deferred because its core resides in an abstraction (the mixin) 553 | instead of being tied to a classname. DoCSSa's components are class agnostic. They are made of mixins. 554 | A component mixin is in charge of binding the reusable styling to one or several HTML class(es). 555 | Thanks to the BEM naming convention, the class passed to the component mixin can be 556 | a prefix for other classes onto which to bind functionalities. 557 |
558 |This allows for components to be instantiated on any class, and to be composable if needed. 559 |
560 |As the component is not tied to a particular class, you can use it on whatever class you want, whenever you want,
561 | from the stylesheet side. That means that you can (and should) keep a semantic meaning to your HTML classes, and change
562 | their look and feel without having to modify the markup.
563 | When your markup comes from a RTE, this is a huge gain.
564 | You can change the styling without asking the contributors to change their habits, and you can
565 | affect everything already written without having to make search&replace throughout a database or without
566 | having a "red" class helper mean the color is going to be purple!
567 |
569 | For example, instead of having a link with the "button red" classes, you can give it a "navTrigger"
570 | class and bind a button component with a red skin to it. You could
571 | also use the same component on a "submitTrigger" class so that both look the same. When time comes
572 | to have a different look for your submits, all you have to do is bind the submitTrigger to another component
573 | and you're done, without affecting the navTriggers and without touching the markup.
574 | Note that for advanced components,
575 | the HTML may be expected to conform to a particular structure to be a valid target.
576 |
If you need a component with a slightly different behaviour than the original one, you can pass 578 | a parameter to the mixin to change the binding (or rules). If the exceptions become too numerous, 579 | it's probably time to consider creating another component or a set of small components. 580 |
581 |Here is what a component definition looks like : 583 |
584 | 585 | // component example 586 |587 | // map the placeholders content to some selectors through a mixin 588 | @mixin example($selector, $defaultSkin: true) { 589 | 590 | #{$selector} { 591 | padding: 10px; 592 | } 593 | 594 | #{$selector}_inner { 595 | padding: 20px; 596 | } 597 | 598 | #{$selector}:hover { 599 | padding: 30px; 600 | } 601 | 602 | @if $defaultSkin != false { 603 | @include example-skin-default($selector, $hover); 604 | } 605 | 606 | } 607 |608 | 609 |
In order to fulfill their role, components need to respect those two guidelines :
611 |-
612 |
A component should be self contained.
613 | 614 |This means that what is outside of the visual boundaries of the 615 | component doesn't belong in the component definition. Typically, things like margins or positioning 616 | should reside in the "specifics" folder, not in the component. This is required for your component to 617 | live in any possible context.
618 |
619 | Structure(layout) should be dissociated from skin(paint).
620 | 621 |For example, background color, images, text colors, etc. may go into the skin section of the 622 | component, not in its core definition. That way, you can create additional styles for them without 623 | altering their expected behaviour as a component, and choose what visual style you want when binding 624 | the component to a classname.
625 |
626 |
Of course, it's up to you to compose with those precepts and adapt them to your constraints, but 628 | respecting them results in a clean separation of concerns and genuinely reusable components, which is much 629 | likely to make your dev faster further down the road. 630 |
631 |
632 | When beginning with DoCSSa, it's easy to think as everything as components, as they are so powerful.
633 | You must be careful about that, and think about what needs to be a component and what doesn't.
634 | If you component-ize every set of rules, you risk spending more time building the same thing as you
635 | would have in the "specifics" folder without enough additional value for it to be worth it.
636 | Try to begin with small components to get the hang of it, and adjust your use little by little.
637 | You can begin everything the way you are used to in the "specifics" folder, organize it
638 | in imported files and subfolders the DoCSSa way, and only extract a functionality to a component
639 | when you feel that it would help you.
640 | DoCSSa only gives you tools to play with, what you implement and how you do things with it is up to you.
641 | That's what we believe a "framework" should be anyway.
642 |
Description
644 |When looking at a component folder, you will see one file, and possibly some subfolders.
645 | Let's take a Tabs component as an example, as probably everyone has already seen a tabbed
646 | navigation in a page. The file in the Tabs folder would be: _tabs.scss.
647 | It is the component definition. Let's look at it in details.
648 |
Component's definition
650 |The component definition file contains two mixins. One for the component structure, one for 651 | the component's skin. 652 |
653 |Here's the structure part of our "tabs" component :
654 | 655 |-
657 |
- 658 | // components/tabs/_tabs.scss 659 | 660 |
662 | 663 | // _____ STRUCTURE _____ // 664 | 665 | // define the component structure 666 | @mixin tabs($selector, $defaultSkin: true) { 667 | 668 | #{$selector} { 669 | display: inline-block; 670 | margin: 0; 671 | padding: 0; 672 | font-size: 0; 673 | } 674 | 675 | #{$selector}_item { 676 | @include remIt(font-size, 18); 677 | display: inline-block; 678 | vertical-align: bottom; 679 | line-height: 2.5; 680 | } 681 | 682 | #{$selector}_link { 683 | padding: 0 10px; 684 | } 685 | 686 | @if $defaultSkin != false { 687 | @include tabs-skin-default($selector); 688 | } 689 | 690 | } 691 |692 | 693 |
A mixin named after the component is all it takes. Its role is to 696 | actually bind the component's styling rules to the provided classname, its "elements", and its state. 697 |
698 |We recommend that the "structure" part of the component doesn't make use of any 699 | project variable in order to stay as generic and reusable as possible. 700 |
701 |By default, the component binding will also implement the default skin. If we have defined another 702 | skin, all we need to do is pass "$defaultSkin: false" when instantiating the component, and 703 | call another skin binding mixin to handle the component's skin. 704 |
705 |We said earlier that a component folder may have subfolders. One of those is a "_skins" 706 | folder that would host different skins that we could import as needed. The "_skins" folder 707 | is prefixed by an underscore only so that it always show on top of the list in case you need 708 | some other folders in there. 709 |
710 |Now let's look at the skin part of our "tabs" component : 711 |
712 | // A component's skin part 713 |714 | // _____ SKIN _____ // 715 | 716 | // provide a default skin for the component 717 | // only visual changes that don't affect the component layout should be in here 718 | @mixin tabs-skin-default($selector) { 719 | 720 | #{$selector} { 721 | list-style: none; 722 | } 723 | 724 | #{$selector}_item { 725 | border-bottom: 1px solid $color-main-lighter; 726 | 727 | &._is_current { 728 | border-bottom: 3px solid $color-main-light; 729 | } 730 | } 731 | 732 | #{$selector}_item--highlighted { 733 | background: $color-alt-light; 734 | } 735 | 736 | #{$selector}_link { 737 | @include basicClickable(true); 738 | border-left: 1px solid $color-main; 739 | color: $color-default-dark; 740 | font-family: 'titillium', Arial, 'sans-serif'; 741 | 742 | #{$selector}_item:first-child & { 743 | border-left: 0; 744 | } 745 | 746 | #{$selector}_item._is_current & { 747 | color: $color-main-light; 748 | } 749 | 750 | &:hover { 751 | background: $color-alt; 752 | } 753 | } 754 | } 755 |756 | 757 |
As you can see, it is very close to the structure part. Actually it is placed inside the component's 758 | folder for simplicity, but it could very well reside in a _skins folder right away. 759 |
760 |As stated before, a component's "structure" should only contain it's layout, it's skeleton.
761 | The "skin" part, unlike the structure, should
762 | only contain rules that don't affect the structure. These rules can be
763 | background-colors, background-images, border-radius, shadows, opacity... Anything you want as long as it doesn't
764 | fiddle with the component's external boundaries.
765 | It is not always easy, but the more you practice it the
766 | more it feels natural, and the easier it is to find out the right place when you come back to adjust
767 | something about your component.
768 |
Implementation
770 |Now that we know how a component is structured, it's time to implement it. 771 | This is done by calling the component mixin and passing it a class selector. 772 |
773 |Here's what an implementation may look like :
774 | // in specifics/_main.scss 775 |776 | @include tabs('.mainMenu'); 777 |778 |
That's right, now that you have defined your component, using it is as easy as that!
779 |And here is what the output css will look like :
780 | 781 | 782 | 783 | 784 | // in css/custom.css 785 |786 | .mainMenu { 787 | display: inline-block; 788 | margin: 0; 789 | padding: 0; 790 | font-size: 0; 791 | } 792 | .mainMenu_item { 793 | font-size: 18px; 794 | display: inline-block; 795 | vertical-align: bottom; 796 | line-height: 2.5; 797 | } 798 | .cssremunit .mainMenu_item { 799 | font-size: 1.125rem; 800 | } 801 | .mainMenu_link { 802 | padding: 0 10px; 803 | } 804 | 805 | 806 | .mainMenu { 807 | list-style: none; 808 | } 809 | .mainMenu_item { 810 | border-bottom: 1px solid #ffdec7; 811 | } 812 | .mainMenu_item._is_current { 813 | border-bottom: 3px solid #ff9e7e; 814 | } 815 | .mainMenu_item--highlighted { 816 | background: #f8f5ec; 817 | } 818 | .mainMenu_link { 819 | background: none; 820 | border: 0; 821 | cursor: pointer; 822 | text-decoration: none; 823 | border-left: 1px solid #ff7f50; 824 | color: #333; 825 | font-family: 'titillium', Arial, 'sans-serif'; 826 | } 827 | .mainMenu_item:first-child .mainMenu_link { 828 | border-left: 0; 829 | } 830 | .mainMenu_item._is_current .mainMenu_link { 831 | color: #ff9e7e; 832 | } 833 | .mainMenu_link:hover { 834 | background: #f1ede4; 835 | } 836 |837 |
If later on you need to bind the tabs component to another class, all you have to do is to call the mixin 838 | with that class and the parameters you want, and you're done! 839 |
840 |For example :
841 | // in specifics/_main.scss 842 |843 | @include tabs('.articleTabs', $defaultSkin: false); 844 | @include tabs-skin-alternate('.articleTabs'); 845 |846 |
851 | 852 |
Naming conventions
856 |HTML classes
860 | 861 |In DoCSSa, we decided to follow BEM class naming convention.
862 | HTML class names for Blocks are lowerCamelCase, and the Elements nested
863 | within are separated by
864 | an underscore(_). Modifiers are separated by a double dash(--), and they are used
865 | for elements
866 | variants. An element's state is written separately with a pattern that
867 | begins with "_is_".
868 |
871 | Variants are dissociated from states because they play a different role,
872 | and it appeared that with our components architecture, having a simple "_is_current" class for our items'
873 | "current" state was way more effective than a "mainMenu_item--current" when it came to modifying the state through
874 | javascript.
875 | BEM modifiers are now exclusively used for visual variants that don't change over time. They
876 | have a meaning at the content(HTML) level. For example, if a tab needs to be highlighted so that
877 | it stands out, it is applied a variant by using a modifier.
878 | By opposition, a "state" is something that is supposed to be handled by javascript and
879 | change over time.
880 |
Here is an example of what that convention may look like in our tab example :
884 | // naming example 885 |886 | <nav class="mainNav"> 887 | <ul class="mainMenu"> 888 | <li class="mainMenu_item"> 889 | <a class="mainMenu_link" href="#">Home</a> 890 | </li> 891 | <li class="mainMenu_item"> 892 | <a class="mainMenu_link" href="#about">About</a> 893 | </li> 894 | <li class="mainMenu_item"> 895 | <a class="mainMenu_link" href="#fileStructure">File Structure</a> 896 | </li> 897 | <li class="mainMenu_item"> 898 | <a class="mainMenu_link" href="#components">Components</a> 899 | </li> 900 | <li class="mainMenu_item _is_current"> 901 | <a class="mainMenu_link" href="#namingConventions">Naming conventions</a> 902 | </li> 903 | <li class="mainMenu_item"> 904 | <a class="mainMenu_link" href="#responsive">Responsive</a> 905 | </li> 906 | <li class="mainMenu_item"> 907 | <a class="mainMenu_link" href="#gettingStarted">Getting started</a> 908 | </li> 909 | <li class="mainMenu_item mainMenu_item--highlighted"> 910 | <a class="mainMenu_link" href="#contributing">Contributing</a> 911 | </li> 912 | </ul> 913 | <button class="mainNav_opener"></button> 914 | </nav> 915 |916 | Note that this example is taken from the menu you're looking at just now in the present 917 | page. 918 |
At first, the BEM notation style can seem quite verbose and put off some developers, but it
920 | is very powerful and allows us to go way behind old school naming conventions.
921 | It has to be tamed, though, and a classic beginner's mistake is to reflect the position of
922 | each element in the component's DOM in its class name. To avoid that, we recommend that you look
923 | at your low level elements first (those deeper in the DOM tree) and wonder if they could exist
924 | elsewhere in the page or in the component.
925 | Going up the tree, you can identify your most basic reusable components and set the names
926 | accordingly.
927 |
930 | For example, in our example above, the "mainMenu_link" class doesn't need to reflect the
931 | fact that it is contained in a "mainMenu_item", this doesn't really matter to the link, so there is no
932 | reason to specify it in its name.
933 | Also, if the element can live outside the block you're looking at, it's probably not a good
934 | idea to tie it to it, and it probably should exist on its own.
935 |
Sass files
938 | 939 |We talked about some of the aspects of the Sass file naming convention in the "File 940 | Structure" section.
941 | 942 |Each file is lowerCamelCase named and prefixed by an underscore(_) so that it is considered 943 | by the Sass compiler as a partial and not rendered to a standalone CSS file. 944 |
945 | 946 |
947 | Each folder has a single entry point in charge of importing other partial Sass files. This
948 | file is named after the folder's name, and is prefixed with two underscores(__) so that it always
949 | appears on top in the files list. It imports only the files in its own folder, except for other summary
950 | indexes located in a direct subfolder. This way, you always can handle your imports very easily and stop the
951 | imports of nested files at any level.
952 | Components definition files should have the same name as the component's folder name, and
953 | have a single underscore prefix, as they are content and not imports list.
954 |
SASS variables
957 | 958 |Sass variable names should be composed of dash(-) separated parts, with each part sorted from 959 | the most generic to the most specific variable characteristic. This is quite useful in most IDE as 960 | you can take advantage of autocompletion. 961 |
962 | 963 |For example :
965 | // variables example 966 |967 | /* _____ VARIABLES _____ */ 968 | 969 | // Generic 970 | // ========================================================================== 971 | 972 | $default-borderRadius: 4; 973 | $containerWidth: 760; 974 | 975 | 976 | 977 | // Colors 978 | // ========================================================================== 979 | 980 | $color-default-light: #fff; 981 | $color-default-dark: #333; 982 | $color-default-darker: #000; 983 | 984 | $color-main: #ff7f50; 985 | $color-main-light: #ff9e7e; 986 | $color-main-lighter: #ffdec7; 987 | 988 | $color-secondary: #5cbcaa; 989 | 990 | $color-alt: #f1ede4; 991 | $color-alt-dark: #c2c0bc; 992 | $color-alt-light: #f8f5ec; 993 | 994 | $color-alt2: #637c84; 995 |996 |
1001 | 1002 |
Responsive
1006 |Introduction
1010 |Now that you're all familiar with the core concepts and basic implementation of DoCSSa, it's time
1011 | to take it to the next level and see how we can handle Responsive Web Design (RWD).
1012 | As you may have noticed, the main menu on the page you are currently looking at changes depending
1013 | on the width of the viewport. We'll use it as an example to show you how to handle RWD.
As always, there isn't a one size fit all solution, and your mileage may vary, but we thought 1015 | it would be nice to provide at least one implementation of a responsive widget.
1016 | 1017 |Dispatching the roles
1018 |In order to have responsive tabs on this page, we had to decide what was going to be part of a
1019 | component, and what would be outside. We chose to create a responsive component that would
1020 | display horizontally from 900px and up, and vertically below that breakpoint.
1021 | As a component should be as reusable as possible, we made that value a parameter of the component
1022 | mixin.
1023 | We also wanted the vertical menu to be hidden by default, and only revealed when clicking on a burger
1024 | icon. This is a specific implementation : all responsive tabs don't need to hide the menu when they
1025 | are in vertical state. As such, we handled the hiding and revealing of the menu in the "specifics" part
1026 | and kept the component unaware of those specifics.
Responsive component
1029 |Here is what our sample component looks like :
1030 | 1031 |-
1033 |
- 1034 | // components/responsiveTabs/_responsiveTabs.scss 1035 | 1036 |
1039 | // _____ STRUCTURE _____ // 1040 | 1041 | // define the component structure 1042 | @mixin responsiveTabs($selector, $breakpoint: 900, $defaultSkin: true) { 1043 | 1044 | // handle tabs vertically until breakpoint 1045 | @media (max-width: #{($breakpoint - 1)}px) { 1046 | 1047 | #{$selector} { 1048 | padding: 0; 1049 | } 1050 | 1051 | #{$selector}_link { 1052 | display: block; 1053 | padding: 20px; 1054 | } 1055 | } 1056 | 1057 | // handle tabs horizontally until breakpoint 1058 | @media (min-width: #{$breakpoint}px) { 1059 | 1060 | #{$selector} { 1061 | display: inline-block; 1062 | margin: 0; 1063 | padding: 0; 1064 | font-size: 0; 1065 | } 1066 | 1067 | #{$selector}_item { 1068 | @include remIt(font-size, 18); 1069 | display: inline-block; 1070 | vertical-align: bottom; 1071 | line-height: 2.5; 1072 | } 1073 | 1074 | #{$selector}_link { 1075 | padding: 0 10px; 1076 | } 1077 | } 1078 | 1079 | @if $defaultSkin != false { 1080 | @include responsiveTabs-skin-default($selector, $breakpoint); 1081 | } 1082 | 1083 | } 1084 | 1085 | // _____ SKIN _____ // 1086 | 1087 | // provide a default skin for the component 1088 | // only visual changes that don't affect the component layout should be in here 1089 | @mixin responsiveTabs-skin-default($selector, $breakpoint) { 1090 | 1091 | #{$selector} { 1092 | list-style: none; 1093 | } 1094 | 1095 | #{$selector}_item { 1096 | border-bottom: 1px solid $color-main-lighter; 1097 | } 1098 | 1099 | #{$selector}_item--highlighted { 1100 | background: $color-alt-light; 1101 | } 1102 | 1103 | #{$selector}_link { 1104 | @include basicClickable(true); 1105 | color: $color-default-dark; 1106 | font-family: 'titillium', Arial, 'sans-serif'; 1107 | 1108 | #{$selector}_item._is_current & { 1109 | color: $color-main-light; 1110 | } 1111 | 1112 | &:hover { 1113 | background: $color-alt; 1114 | } 1115 | } 1116 | 1117 | // set specific visual adjustments for horizontal display 1118 | @media (min-width: #{$breakpoint}px) { 1119 | 1120 | #{$selector}_item { 1121 | &._is_current { 1122 | border-bottom: 3px solid $color-main-light; 1123 | } 1124 | } 1125 | 1126 | #{$selector}_link { 1127 | border-left: 1px solid $color-main; 1128 | 1129 | #{$selector}_item:first-child & { 1130 | border-left: 0; 1131 | } 1132 | } 1133 | } 1134 | 1135 | } 1136 | 1137 |1138 |
As usual, the component is split in two parts: the structure and the skin. Its implementation 1140 | is located in specifics/_nav.scss: 1141 |
1142 | @include responsiveTabs('.mainMenu', $breakpoint: 900); 1143 |1144 |
As an experiment, you can add a "$defaultSkin: false" parameter to the call to see 1145 | how the component looks like with no skin applied. You'll see that it behaves the same, but 1146 | doesn't look as good. 1147 |
1148 |On the structure part, things are quite simple: we declare a totally different set of 1149 | rules for below or above the breakpoint. It is not mandatory, but can help avoiding regression 1150 | in the long run.
1151 |On the skin part, we tried to keep most rules generic. We wanted both layout to have the 1152 | same look&feel. Still, some visual enhancements only made sense in the horizontal 1153 | display, so we made sure they got scoped to the correct media query.
1154 | 1155 |Responsive implementation
1156 | 1157 |Once we have a responsive component, all we have to do is give it some boundaries.
1158 | In the "specifics" section we call the component and pass it a breakpoint. We also set some
1159 | rules to the "mainNav" wrapper according to this breakpoint: the position
1160 | of the tabs in the page, and the visual enhancement that go with it. For example, we set a
1161 | bottom drop shadow for the horizontal state of the menu, and a right drop shadow for its
1162 | vertical state. We also make sure the burger menu toggler is hidden when we don't need it,
1163 | and the vertical menu respects a specific width.
-
1167 |
- 1168 | // specifics/_nav.scss 1170 | 1171 |
1174 | $nav-breakpoint: 900; 1175 | $left-nav-width: 200; 1176 | 1177 | // bind the responsive tabs mixin to the .mainMenu class 1178 | @include responsiveTabs('.mainMenu', $breakpoint: 900); 1179 | 1180 | // handle menu position and visibility 1181 | @media (max-width: #{($nav-breakpoint - 1)}px) { 1182 | .mainNav { 1183 | @include transition('left .2s linear'); 1184 | @include box-shadow(0 3px 3px 3px rgba(0, 0, 0, .3)); 1185 | position: absolute; 1186 | top: 0; 1187 | bottom: 0; 1188 | left: -#{$left-nav-width}px; 1189 | z-index: 999; 1190 | background: $color-default-light; 1191 | width: #{$left-nav-width}px; 1192 | 1193 | &._is_open { 1194 | left: 0; 1195 | } 1196 | } 1197 | 1198 | .mainNav_opener { 1199 | display: block; 1200 | position: absolute; 1201 | top: 20px; 1202 | left: #{$left-nav-width + 20}px; 1203 | z-index: 998; 1204 | border: 1px solid $color-default-light; 1205 | background: $color-main; 1206 | padding: 0; 1207 | width: 30px; 1208 | height: 30px; 1209 | overflow: hidden; 1210 | text-indent: -999px; 1211 | color: $color-default-light; 1212 | 1213 | &:before { 1214 | display: block; 1215 | width: 28px; 1216 | height: 30px; 1217 | text-align: center; 1218 | text-indent: 0; 1219 | line-height: 1.8; 1220 | content: '\2630'; 1221 | } 1222 | 1223 | .mainNav._is_open &:before { 1224 | content: '\2613'; 1225 | } 1226 | } 1227 | } 1228 | 1229 | @media (min-width: #{$nav-breakpoint}px) { 1230 | 1231 | .mainNav { 1232 | @include box-shadow('0 3px 5px rgba(0,0,0,.3)'); 1233 | @include box-opacity(.95); 1234 | position: fixed; 1235 | top: 0; 1236 | right: 0; 1237 | bottom: auto; 1238 | left: 0; 1239 | background: $color-default-light; 1240 | text-align: right; 1241 | } 1242 | 1243 | .mainNav_opener { 1244 | display: none; 1245 | } 1246 | 1247 | } 1248 |1249 |
All there is left to do is use a bit of javascript to drive the state of the mainNav wrapper, 1252 | and voila, a responsive menu we have!
1253 | 1254 |1259 | 1260 |
Getting started
1264 |Download the kit
1269 | Download this page and its implementation of DoCSSa and play with it.1270 | Source code can be found on 1271 | DoCSSa's github page. 1272 |
Learn about the basics
1273 |-
1274 |
- 1275 | 1280 | 1281 |
-
1282 | 1283 | 1284 | Why DoCSSa ? [article] 1285 | 1286 |1287 |
1288 |
-
1289 | 1290 | 1291 | Getting started with SASS [article] 1292 | 1293 |1294 |
1295 |
- 1296 | 1301 | 1302 |
-
1303 | 1304 | 1305 | BEM methodology [official website] 1306 | 1307 |1308 |
1309 |
- 1310 | 1315 | 1316 |
How to start ?
1318 |-
1319 |
-
1320 | 1321 | First, you can simply write CSS code in separated files into the "specifics" folder, 1322 | using DoCSSa's logic. 1323 |1324 |
1325 |
-
1326 | 1327 | Then, try manipulating the provided "_mixins" from the base/utils folder 1328 | to give your project more reusable code. 1329 |1330 |
1331 |
-
1332 | 1333 | After that, you may write your own "_mixins", tailored for your own 1334 | specific needs. 1335 |1336 |
1337 |
-
1338 | 1339 | For more reusability within or across your project(s), you may then try to convert 1340 | some of your "specifics" rules into "components".1343 |
1341 | If see you are repeating the same rules on different sets of classes, you have a good candidate. 1342 |
1344 | -
1345 | 1346 | Been there, done that ? It's time to preach the choir ;) 1347 |1348 |
1349 |
I'm the team lead of a non expert koala army, how can I make use of DoCSSa's powers ?
1352 |-
1353 |
-
1354 | 1355 | While your koalas are learning Sass, they can keep writing the same old CSS in 'specifics' folder 1356 |1357 |
1358 |
-
1359 | 1360 | Once one of them feels ready to, let her write a component and let the whole team know 1361 | they can use it. 1362 |1363 |
1364 |
-
1365 | 1366 | It won't be long before some other koalas would like to write a useful component too. 1367 |1368 |
1369 |
-
1370 | 1371 | With knowledge adding up, it will become easier to detect components candidates. 1372 |1373 |
1374 |
-
1375 | 1376 | Don't forget to offer your koalas a lot of eucalyptus regularly. 1377 |1378 |
1379 |
1385 | 1386 |
Contributing
1390 |Contact us
1395 | 1396 |DoCSSa is evolving!
1397 | We are very interested in your feedback to improve it or learn about how you implemented
1398 | it.
1399 | Please contact us, fork
1400 | the project, make Pull Requests
1401 | or talk about DoCSSa at will.
1402 |