Sass Guidelines

Ένα δογματικό styleguide για συγγραφή ισορροπημένης, συντηρήσιμης και επεκτάσιμης Sass.

Βλέπετε την ελληνική μετάφραση των Μόδεστο Καφφέ, Σεμπαστιέν Νικολάου, Άδωνη Κακουλίδη και Κωνσταντίνου Μαργαρίτη για τις πρωτότυπες Οδηγίες για Sass των Kitty Giraudel.

Αυτή η έκδοση συντηρείται αποκλειστικά από συνεισφέροντες χωρίς τον έλεγχο του κύριου συντάκτη, επομένως μπορεί να μην είναι εντελώς αυθεντική.

Ανοίξτε τον πίνακα επιλογών

Σχετικά με τον συγγραφέα

Ονομάζομαι Kitty Giraudel, είμαι ένας front-end developer με έδρα το Βερολίνο (Γερμανία) απο το 2015, αυτή τη στιμγή εργάζομαι για την Cofenster.

Γράφω Sass αρκετά χρόνια και έχω δημιουργήσει πολλά projects σχετικά με την Sass όπως το SassDoc, SitePoint Sass Reference και το Sass-Compatibility. Αν σ’ ενδιαφέρει να δεις περισσότερα για τη συνεισφορά μου στην κοινότητα της Sass, ρίξε μια ματιά σ’ αυτήν τη λίστα.

Επίσης είμαι ο συγγραφέας ενός βιβλίου για CSS (στα γαλλικά) που ονομάζεται CSS3 Pratique du Design Web (Εκδόσεις Eyrolles), όπως και ενός βιβλίου για Sass (στα αγγλικά) που ονομάζεται Jump Start Sass (Εκδόσεις Learnable).

Πως μπορείτε να συνεισφέρετε

Το Sass Guidelines είναι ένα ελεύθερο project που συντηρώ στον ελεύθερό μου χρόνο. Περιττό να πω ότι απαιτείται πάρα πολλή δουλειά για να διατηρηθεί το project ενημερωμένο, τεκμηριωμένο και συναφές. Ευτυχώς, έχω τη βοήθεια πολλών σπουδαίων ανθρώπων που συνεισφέρουν, ειδικά όσον αφορά τη συντήρηση δεκάδων διαφορετικών μεταφράσεων. Be sure to thank them!

Τώρα αν ενδιαφέρεστε να συνεισφέρετε, παρακαλώ γνωρίζετε πως ένα tweet που αναφέρει το project, ή την διάδοσή του, ακόμα και η διόρθωση ενός ορθογραφικού λάθους ανοίγοντας ένα issue ή pull-request στο GitHub repository θα ήταν υπέροχο!

Τέλος αν απολαύσατε αυτό το έγγραφο ή αν ήταν χρήσιμο σε εσάς ή την ομάδα σας, παρακαλώ σκεφτείτε το ενδεχόμενο να το υποστηρίξετε για να συνεχίσω αυτή την προσπάθεια!

Σχετικά με τη Sass

Η Sass αυτοπροσδιορίζεται στο documentation ως εξής:

Η Sass είναι μία επέκταση της CSS η οποία προσθέτει ισχύ και κομψότητα στη βασική γλώσσα.

Ο απώτερος στόχος της είναι να διορθώσει τα ελλατώματα της CSS. Όπως όλοι γνωρίζουμε, Η CSS δεν είναι και η καλύτερη γλώσσα στον κόσμο [εκκρεμεί παραπομπή]. Αν και είναι πολύ απλή στην εκμάθηση, μπορεί να γίνει ακατάστατη πολύ γρήγορα, ειδικά σε μεγάλα projects.

Σ’αυτό το σημείο έρχεται η Sass, ως μία μετα-γλώσσα, για να βελτιώσει τη σύνταξη της CSS ώστε να της παρέχει επιπλέον δυνατότητες και βολικά εργαλεία. Παράλληλα, η Sass επιδιώκει να παραμείνει συντηρητική σε σχέση με τη γλώσσα CSS.

Το ζητούμενο δεν είναι να μετατρέψει τη CSS σε μία γλώσσα προγραμματισμού πλήρη δυνατοτήτων· η Sass επιδιώκει να βοηθήσει μόνο εκεί που η CSS αποτυγχάνει. Εξαιτίας αυτού, το να ξεκινήσεις να μαθαίνεις Sass δεν είναι πιο δύσκολο από το να μαθαίνεις CSS: απλά προσθέτει μερικές επιπλέον δυνατότητες πάνω στις ήδη υπάρχουσες.

Εντούτοις, υπάρχουν πολλοί τρόποι να χρησιμοποιήσουμε αυτές τις δυνατότητες. Κάποιοι καλοί, κάποιοι κακοί και κάποιοι ασυνήθιστοι. Αυτά τα guidelines έχουν σκοπό να σου δώσουν μία συνεκτική και τεκμηριωμένη προσέγγιση για τη συγγραφή κώδικα σε Sass.

Ruby Sass ή LibSass

Το πρώτο commit της Sass ανάγεται στα τέλη του 2006, πάνω από 10 χρόνια πριν. Περιττό να πούμε ότι έχει διανύσει πολύ δρόμο από τότε. Αρχικά αναπτύχθηκε σε Ruby, και διάφορα ports ξεφύτρωσαν από δω κι από κει. Το πιο δημοφιλές, το LibSass (γραμμένο σε C/C++), είναι πλέον κοντά στο να είναι πλήρως συμβατό με την πρωτότυπη έκδοση σε Ruby.

Το 2014, οι ομάδες του Ruby Sass και του LibSass αποφάσισαν να περιμένουν και τις δύο εκδόσεις να συγχρονιστούν πριν προχωρήσουν παραπέρα. Από τότε, το LibSass δημοσιεύει τακτικά νέες εκδόσεις μέχρι να φτάσει τον μεγαλύτερο αδερφό του σε δυνατότητες. Οι τελευταίες ασυνέπειες που έχουν απομείνει έχουν συγκεντρωθεί και τις παραθέτω στο Sass-Compatibility project. Αν έχεις υπόψη σου κάποια ασυνέπεια μεταξύ των δύο εκδόσεων που δεν αναφέρεται, μπορείς να ανοίξεις ένα issue αν έχεις την καλοσύνη.

Επιστρέφουμε στην επιλογή του compiler. Βασικά εξαρτάται από το project σου. Αν είναι ένα project σε Ruby on Rails, καλύτερα να χρησιμοποιήσεις το Ruby Sass, που ταιριάζει απόλυτα στην περίπτωση. Επίσης, να ξέρεις ότι το Ruby Sass θα είναι πάντα το σημείο αναφοράς όσον αφορά την υλοποίηση της Sass και πάντα θα προηγείται του LibSass σε δυνατότητες. Αν θέλετε να μεταβείτε απο Ruby Sass σε LibSass, αυτό το άρθρο είναι για σας.

Όταν θέλεις να εντάξεις τη Sass στη ροή εργασίας σου σε κάποιο project που δεν είναι σε Ruby, το LibSass είναι πιθανώς καλύτερη ιδέα επειδή είναι εξειδικευμένο γι’ αυτή την περίπτωση. Οπότε αν θες να χρησιμοποιήσεις ας πούμε NodeJS, το node-sass είναι το πιο επιθυμητό.

Sass ή SCSS

Επικρατεί μεγάλη σύγχυση σχετικά με τη σημειολογία της ονομασίας Sass, για έναν καλό λόγο: ως Sass εννοούμε και τον preprocessor και τη σύνταξη της γλώσσας. Λίγο άβολο, έτσι δεν είναι;

Βλέπεις, η Sass αρχικά περιέγραφε μία σύνταξη στην οποία το βασικό χαρακτηριστικό ήταν η ευαισθησία στο indentation. Σύντομα οι συντηρητές της Sass αποφάσισαν να γεφυρώσουν το χάσμα μεταξύ Sass και CSS παρέχοντας μια σύνταξη φιλική ως προς τη CSS με το όνομα SCSS, που σημαίνει Sassy CSS (ζωηρή CSS). Το σύνθημα είναι το εξής: αν είναι έγκυρη CSS, τότε είναι έγκυρη SCSS.

Από τότε, η Sass (ο preprocessor) παρέχει δύο διαφορετικές συντάξεις: τη Sass (χωρίς κεφαλαία παρακαλώ), επίσης γνωστή ως την indented σύνταξη, και την SCSS. Το ποιά σύνταξη θα χρησιμοποιήσεις είναι στο χέρι σου μιας και οι δύο έχουν ακριβώς τις ίδιες δυνατότητες. Σ’ αυτό το σημείο είναι θέμα προτίμησης.

Η whitespace-sensitive σύνταξη της Sass βασίζεται στο indentation για να απαλλαγεί από τα άγκιστρα (braces), τα ερωτηματικά (semi-colons) και άλλα σημεία στίξης, πράγμα που οδηγεί σε μια πιο λιτή και πιο σύντομη σύνταξη. Παράλληλα, η SCSS είναι πιο εύκολη στην εκμάθηση μιας και πρόκειται για μερικά μικρά επιπλέον κομμάτια πάνω στη CSS.

Εγώ προσωπικά προτιμώ την SCSS από τη Sass γιατί είναι πιο κοντά στη CSS και πιο φιλική για τους περισσότερους developers. Γι’ αυτό το λόγο, θα χρησιμοποιώ SCSS αντί για Sass σ’ αυτά τα guidelines.

Λοιποί preprocessors

Η Sass είναι ένας preprocessor μεταξύ άλλων. Ο πιο σοβαρός ανταγωνιστής της είναι η Less, ένας preprocessor που βασίζεται σε NodeJS, ο οποίος έχει γίνει αρκετά δημοφιλής χάρη στο γνωστό CSS framework Bootstrap που το χρησιμοποιεί (μέχρι την 4η έκδοση). Επίσης υπάρχει και το Stylus, ένας πολύ ανεκτικός και ευέλικτο preprocessor όμως ελαφρώς δυσκολότερο στην χρήση και με μικρότερη κοινότητα.

Το γιατί να επιλέξω Sass αντί κάποιου άλλου preprocessor είναι μία βάσιμη απορία ακόμα και σήμερα. Μέχρι πρόσφατα συνιστούσαμε Sass για projects σε Ruby γιατί ο preprocessor ήταν φτιαγμένος σε Ruby και κούμπωνε καλά με το Ruby on Rails. Τώρα που το LibSass έχει συμβαδίσει (σχεδόν) με το πρωτότυπο Sass, πλέον δεν τίθεται τέτοιο θέμα.

Αυτό που μ’ αρέσει στη Sass είναι η συντηρητική της προσέγγιση απέναντι στη CSS. Ο σχεδιασμός της Sass βασίζεται σε ισχυρές αρχές: οι περισσότερες σχεδιαστικές προσεγγίσεις προέρχονται φυσικά από τις πεποιθήσεις των ιδρυτών της πως α) το να προσθέτεις επιπλέον δυνατότητες έχει μία επιβάρυνση σε πολυπλοκότητα η οποία πρέπει να δικαιολογείται από τη χρησιμότητα και β) πρέπει να είναι απλό να κατανοήσεις τι κάνει ένα συγκεκριμένο κομμάτι από styles μελετώντας το απομονωμένα. Επίσης, η Sass δίνει πολύ αυστηρότερη έμφαση στη λεπτομέρεια σε σχέση με άλλους preprocessors. Απ’ όσο μπορώ να πω, οι βασικοί σχεδιαστές νοιάζονται πολύ για την υποστήριξη της κάθε παραμικρής συμβατότητας με τη CSS και φροντίζουν η γενική συμπεριφορά της γλώσσας να είναι συνεπής.

Πέρα από τους preprocessors, πρέπει επίσης να επισημάνουμε εργαλεία όπως το PostCSS και το cssnext τα οποία έχουν γίνει αρκετά δημοφιλή τους τελευταίους μήνες.

Το PostCSS συνηθίζεται να αποκαλείται (λανθασμένα) ως “postprocessor”. Η υπόθεση, συνδιασμένη με το ατυχές όνομα, είναι πως το PostCSS κάνει parse τη CSS η οποία έχει ήδη επεξεργαστεί από έναν preprocessor. Ένω μπορεί να δουλέψει και με αυτόν τον τρόπο, δεν είναι απαιτούμενο και έτσι το PostCSS είναι στην πραγματικότητα απλώς ένας “processor”.

Παρέχει την πρόσβαση σε “tokens” των stylesheets σας (όπως τους selectors, τα properties και τις τιμές), τα επεξεργάζεται μέσω JavaScript για να εκτελέσει κάποια εργασία και (τέλος) κάνει compile τα αποτελέσματα σε CSS. Για παράδειγμα, η δημοφιλής prefixing βιβλιοθήκη Autoprefixer είναι φτιαγμένη με PostCSS. Κάνει parse κάθε κανόνα CSS για να δει αν χρειάζονται vendor prefixes με βάση το εργαλείο υποστήριξης χαρακτηριστικών στους browsers CanIUse, κι έπειτα διαγράφει vendor prefixes που δεν χρειάζονται και προσθέτει αυτά που χρειάζονται.

Αυτό είναι εξαιρετικά ισχυρό και καλό για την δημιουργία βιβλιοθηκών που λειτουργούν με οποιονδήποτε preprocessor (όπως επίσης και με απλή CSS), αλλά το PostCSS δεν είναι ιδιαίτερα εύκολο στη χρήση ακόμη. Πρέπει να γνωρίζετε Javascript για μπορείτε να χτίσετε κάτι με αυτό, και το API του μπορεί να σας μπερδέψει κατά καιρούς. Ενώ η Sass παρέχει μόνο ένα σετ από χαρακτηριστικά που είναι χρήσιμα για να γράψει κάποιος CSS, το PostCSS παρέχει απευθείας πρόσβαση στο CSS AST (abstract syntax tree) και στη JavaScript.

Εν συντομία, η Sass είναι σχετικά εύκολη και θα λύσει τα περισσότερα από τα προβλήματά σας. Από την άλλη, το PostCSS είναι αρκετά δύσκολο στην κατανόηση (ιδίως αν δε γνωρίζετε JavaScript), αλλά αποδεικνύεται ότι είναι ένα εξαιρετικά ισχυρό εργαλείο. Δεν υπάρχει κάποιος λόγος για τον οποίο δε μπορείτε και δεν πρέπει να χρησιμοποιείτε και τα δύο. Στην πραγματικότητα, το PostCSS προσφέρει επίσημα SCSS parser γι’ αυτόν ακριβώς το λόγο.

Ευχαριστώ τον Cory Simmons για τη βοήθεια και την εμπειρογνωμοσύνη που πρόσφερε σε αυτό το τμήμα.

Εισαγωγή

Λόγοι για ένα styleguide

Ένα styleguide δεν είναι ένα έγγραφο απλά ευχάριστο στην ανάγνωση, που απεικονίζει μια ιδανική κατάσταση για τον κώδικά σου. Είναι ένα έγγραφο βασικής σημασίας για τη ζωή ενός project, το οποίο περιγράφει γιατί και πώς πρέπει να γράφεται ο κώδικας. Μπορεί να ακούγεται πλεονασμός για μικρά projects, αλλά βοηθάει πολύ στη διατήρηση ενός καθαρού, επεκτάσιμου και ευκολοσυντήρητου κώδικα.

Περιττό να πούμε πως όσο περισσότεροι developers εμπλέκονται σε ένα project, τόσο περισσότερο χρειάζονται guidelines για τον κώδικα. Στα ίδια πλαίσια, όσο μεγαλύτερο το project, τόσο αναγκαίο καθιστάται ένα styleguide.

Ο Harry Roberts αναφέρει πολύ καλά στο CSS Guidelines:

Ένα code styleguide (σημ., όχι οπτικό styleguide) είναι ένα πολύτιμο εργαλείο για ομάδες οι οποίες:

  • αναπτύσσουν και συντηρούν προϊόντα για ένα εύλογο χρονικό διάστημα·
  • απασχολούν developers με διαφορετικές ικανότητες και ειδικότητες·
  • απασχολούν έναν αριθμό από διαφορετικούς developers οι οποίοι εργάζονται πάνω σε ένα προϊόν ανά πάσα στιγμή·
  • προσλαμβάνουν τακτικά νέο προσωπικό·
  • έχουν έναν αριθμό από codebases στα οποία οι developers συχνάζουν.

Disclaimer

Πρώτα απ’ όλα: δεν πρόκειται για ένα CSS styleguide. Αυτό το έγγραφο δεν εξετάζει συμβάσεις ονοματοδοσίας για κλάσεις CSS, modular patterns, ούτε το ζήτημα του ID στη σφαίρα της CSS. Αυτά τα guidelines έχουν μοναδικό στόχο να εξετάσουν θέματα ειδικά για τη Sass.

Επίσης, αυτό το styleguide είναι δικό μου και επομένως ιδιαίτερα δογματικό. Δες στο σαν μία συλλογή από μεθοδολογίες και συμβουλές τις οποίες έχω τελειοποιήσει και έχω προσφέρει την πάροδο των χρόνων. Μου δίνεται επίσης η ευκαιρία να παραθέσω κάποιες αξιοσημείωτες πηγές, οπότε μην αμελήσετε να διαβάσετε τα περαιτέρω αναγνώσματα.

Προφανώς, αυτός δεν είναι ο μοναδικός τρόπος να αντιμετωπίσεις τα πράγματα, οπότε μπορεί να αρμόζει ή να μην αρμόζει στο project σου· επέλεξε ελεύθερα και οτιδήποτε προσάρμοσέ το στις ανάγκες σου. Όπως λέμε, your mileage may vary.

Βασικές αρχές

Στο τέλος της μέρας, αν μπορείς να συγκρατήσεις ένα πράγμα απ’ όλο το styleguide, είναι ότι η Sass πρέπει να είναι όσο πιο απλή γίνεται.

Χάρη στα ανόητα πειράματά μου όπως οι bitwise operators, οι iterators and generators και ένας JSON parser σε Sass, ξέρουμε όλοι τι μπορεί να επιτευχθεί με αυτόν τον preprocessor.

Παράλληλα, η CSS είναι μια απλή γλώσσα. Η Sass που προορίζεται για την εξαγωγή CSS, δεν θα έπρεπε να είναι αρκετά πιο περίπλοκη απο την CSS. Η αρχή KISS (Keep It Simple Stupid) είναι το κλειδί και μπορεί να πάρει προτεραιότητα έναντι της DRY αρχής (Don’t Repeat Yourself) σε κάποιες περιπτώσεις.

Μερικές φορές, είναι καλύτερο να επαναλάμβανόμαστε λίγο για να κρατήσουμε τον κώδικα συντηρήσιμο, αντι να φτιάξεις κάτι βαρή, δυσμεταχείριστος και αδικαιολόγητα περίπλοκο σύστημα που είναι αδύνατο να συντηρηθεί επειδή είναι υπερβολικά περίπλοκο.

Επίσης, και επιτρέψτε μου να παραθέσω τον Harry Roberts για ακόμη μια φορά, ο πραγματισμός υπερέχει τις τελειότητας. Μετά από κάποιο σημείο, μάλλον θα βρείτε τον εαυτό σας να πηγαίνει ενάντια στα πράγματα που περιγράφονται εδώ. Αν βγάζουν νόημα και πιστεύετε πως είναι ορθά, μην διστάσετε να το κάνετε. Ο κώδικας είναι ένα μέσο, όχι αυτοσκοπός.

Επέκταση των guidelines

Ένα μεγάλο μέρος αυτού του styleguide είναι αρκετά δογματικό. Έχω διαβάσει και γράψει Sass αρκετά χρόνια τώρα, σε βαθμό πού τώρα έχω πολλές αρχές όσον αφορά τη συγγραφή καθαρών stylesheets. Καταλαβαίνω ότι μπορεί να μην ικανοποιεί ούτε να ταιριάζει σε όλους, και αυτό είναι απολύτως φυσιολογικό.

Αν και, πιστεύω πως τα guidelines φτιάχνονται για να επεκταθούν. Επεκτείνοντας τα Sass Guidelines θα μπορούσε να είναι τόσο απλό όσο έχοντας ένα έγγραφο που πιστοποιεί ότι ο κώδικας ακολουθεί guidelines απο αυτό το styleguide εκτός από μερικά πράγματα· περίπτωση στην οποία ειδικοί κανόνες θα εξηγούνται παρακάτω.

Ένα παράδειγμα απο την επέκταση του styleguide μπορεί να βρεθεί στο SassDoc repository:

Είναι μια επέκταση του Node Styleguide από τον Felix Geisendörfer. Οτιδήποτε από αυτό το έγγραφο υπερισχύει των λεγομένων του Node Styleguide.

Σύνταξη & μορφοποίηση

Αν με ρωτάς, το πρώτο πράγμα που πρέπει να κάνει ένα styleguide είναι να περιγράφει τον τρόπο που θέλουμε να φαίνεται ο κώδικάς μας.

Όταν διάφοροι developers συμμετέχουν στην συγγραφή CSS στο ίδιο project, είναι απλά θέμα χρόνου μέχρι ένας από αυτούς να ξεκινήσει να δρα με το δικό του τρόπο. Τα guidelines κώδικα που προωθούν την ομοιομορφία όχι μόνο προλαμβάνουν κάτι τέτοιο, αλλά επίσης βοηθούν στο διάβασμα και την ενημέρωση του κώδικα.

Γενικά, θέλουμε (εμπνευσμένα από τα CSS Guidelines):

  • δύο (2) κενά για indentation, όχι tabs,
  • ιδανικά, 80 χαρακτήρες ανά γραμμή,
  • ορθά γραμμένη CSS πολλαπλών γραμμών,
  • εποικοδομητική χρήση κενών,
// Yep
.foo {
  display: block;
  overflow: hidden;
  padding: 0 1em;
}

// Nope
.foo {
    display: block; overflow: hidden;

    padding: 0 1em;
}
// Εφόσον η εμφωλευμένη σύνταξη Sass επιβάλει αυτά τα πρότυπα κώδικα
// Δεν υπάρχει λάθος τρόπος για να προχωρήσεις
.foo
  display: block
  overflow: hidden
  padding: 0 1em

Strings

Είτε το πιστεύεις είτε όχι, τα strings παίζουν πολύ μεγάλο ρόλο και στο οικοσύστημα της CSS αλλά και της Sass. Οι περισσότερες τιμές CSS είναι είτε μήκη ή αναγνωριστικά, οπότε στην πραγματικότητα είναι πολύ σημαντικό να ακολουθούμε κάποια guidelines όταν έχουμε να κάνουμε με strings στη Sass.

Κωδικοποίηση

Για την αποφυγή πιθανού προβλήματος με την κωδικοποίηση χαρακτήρων, συνιστάται ιδιαίτερα να εφαρμοστεί κωδικοποίηση UTF-8 στο main stylesheet χρησιμοποιώντας το @charset directive. Βεβαιώσου ότι είναι το πρώτο πρώτο στοιχείο του stylesheet και ότι δεν υπάρχει κάποιος χαρακτήρας οποιουδήποτε είδους πριν από αυτό.

@charset 'utf-8';
@charset 'utf-8'

Εισαγωγικά

Η CSS δεν απαιτεί τα strings να έχουν εισαγωγικά, ακόμη και αυτά που περιέχουν κενά. Πάρε για παράδειγμα τα font-family names: δεν έχει σημασία αν περικλείεις εσύ τα strings με εισαγωγικά ή ο CSS parser.

Εξαιτίας αυτού, ούτε η Sass απαιτεί τα strings να έχουν εισαγωγικά. Ακόμη καλύτερα (και ευτυχώς πρέπει να παραδεχτούμε), ένα string με εισαγωγικά είναι αυστηρά ισοδύναμο με ένα χωρίς εισαγωγικά (π.χ. το 'abc' είναι αυστηρά ισοδύναμο με το abc).

Παρόλα αυτά, οι γλώσσες που δεν απαιτούν τα strings να έχουν εισαγωγικά είναι σαφώς μειοψηφία οπότε, τα strings πρέπει πάντοτε να περικλείονται από μονά εισαγωγικά (') στη Sass (μονά επειδή είναι πιο εύκολο από τα διπλά να πληκτρολογηθούν στα qwerty πληκτρολόγια). Εκτός της ομοιομορφίας με άλλες γλώσσες, συμπεριλαμβανομένης της συγγενούς με τη CSS Javascript, υπάρχουν διάφοροι λόγοι γι’ αυτή την επιλογή:

  • τα ονόματα των χρωμάτων αντιμετωπίζονται σαν χρώματα όταν είναι χωρίς εισαγωγικά, πράγμα που μπορεί να οδηγήσει σε σοβαρά προβλήματα·
  • οι περισσότεροι syntax highlighters κολλάνε στα strings χωρίς εισαγωγικά·
  • βοηθάει στην γενικότερη αναγνωσιμότητα·
  • δεν υπάρχει κάποιος βάσιμος λόγος για να μην χρησιμοποιούμε εισαγωγικά.
// Yep
$direction: 'left';

// Nope
$direction: left;
// Yep
$direction: 'left'

// Nope
$direction: left

Σύμφωνα με τις προδιαγραφές της CSS, το @charset directive πρέπει να δηλώνεται με διπλά εισαγωγικά για να θεωρείται έγκυρο. Παρόλα αυτά, η Sass φροντίζει γι’ αυτό όταν κάνει compile σε CSS οπότε ο τρόπος που θα το γράψεις δεν έχει αντίκτυπο στο τελικό αποτέλεσμα. Μπορείς να μείνεις με ασφάλεια στα μονά εισαγωγικά, ακόμη και για το @charset.

Strings σαν τιμές CSS

Ορισμένες τιμές (αναγνωριστικά) CSS όπως το initial ή το sans-serif απαιτούν να μην έχουν εισαγωγικά. Πράγματι, η δήλωση font-family: 'sans-serif' θα αποτύχει χωρίς προειδοποίηση επειδή η CSS περιμένει ένα identifier, όχι ένα string σε εισαγωγικά. Εξαιτίας αυτού, δεν βάζουμε εισαγωγικά σε αυτές τις τιμές.

// Yep
$font-type: sans-serif;

// Nope
$font-type: 'sans-serif';

// Okay I guess
$font-type: unquote('sans-serif');
// Yep
$font-type: sans-serif

// Nope
$font-type: 'sans-serif'

// Okay I guess
$font-type: unquote('sans-serif')

Συνεπώς, μπορούμε να κάνουμε μια διάκριση μεταξύ strings που προορίζονται για χρήση ως τιμές CSS (CSS identifiers) όπως στο προηγούμενο παράδειγμα, και strings όταν παραμένουμε σε τύπο αρχείου Sass, για παράδειγμα map keys.

Δεν βάζουμε εισαγωγικά στα πρώτα, όμως στα δεύτερα βάζουμε μονά εισαγωγικά.

Strings που περιέχουν εισαγωγικά

Αν ένα string περιέχει ένα ή περισσότερα μονά εισαγωγικά, μπορεί κανείς να περικλείσει το string με διπλά εισαγωγικά ("), για να αποφύγει να κάνει escape σε χαρακτήρες μέσα στο string.

// Okay
@warn 'You can\'t do that.';

// Okay
@warn "You can't do that.";
// Okay
@warn 'You can\'t do that.'

// Okay
@warn "You can't do that."

URLs

Τα URL θα πρέπει επίσης να έχουν εισαγωγικά, για τους ίδιους λόγους με παραπάνω:

// Yep
.foo {
  background-image: url('/images/kittens.jpg');
}

// Nope
.foo {
  background-image: url(/images/kittens.jpg);
}
// Yep
.foo
  background-image: url('/images/kittens.jpg')

// Nope
.foo
  background-image: url(/images/kittens.jpg)

Αριθμοί

Στη Sass, ο αριθμός (number) είναι ένας τύπος δεδομένων που περιλαμβάνει τα πάντα, από αριθμούς χωρίς μονάδες μέχρι μήκη, διάρκειες, συχνότητες, γωνίες κτλ. Αυτό επιτρέπει να γίνονται υπολογισμοί σε αυτά τα μέτρα.

Μηδενικά

Οι αριθμοί θα πρέπει να εμφανίζονται με μηδενικά πριν από μια δεκαδική τιμή μικρότερη της μονάδας. Ποτέ μην εμφανίζεις μηδενικά στο τέλος ενός δεκαδικού.

// Yep
.foo {
  padding: 2em;
  opacity: 0.5;
}

// Nope
.foo {
  padding: 2.0em;
  opacity: .5;
}
// Yep
.foo
  padding: 2em
  opacity: 0.5

// Nope
.foo
  padding: 2.0em
  opacity: .5

Στο Sublime Text και σε άλλους editors που παρέχουν αναζήτηση και αντικατάσταση με regular expressions, είναι πολύ εύκολο να προσθέσουμε ένα μηδενικό στους (περισσότερους, αν όχι όλους) αριθμούς κινητής υποδιαστολής. Απλά αντικατάστησε το \s+\.(\d+) με \ 0.$1. Μην ξεχάσεις όμως το κενό πριν το 0.

Μονάδες

Όταν έχουμε να κάνουμε με μήκη, μια τιμή 0 δεν πρέπει ποτέ μα ποτέ να έχει μονάδα.

// Yep
$length: 0;

// Nope
$length: 0em;
// Yep
$length: 0

// Nope
$length: 0em

Προσοχή, αυτή η πρακτική πρέπει να περιορίζεται μόνο στα μήκη. Το να έχεις ένα μηδέν χωρίς μονάδα για ένα property χρόνου όπως το transition-delay δεν επιτρέπεται. Θεωρητικά, αν ένα μηδέν χωρίς μονάδα οριστεί για διάρκεια, η δήλωση θεωρείται άκυρη και θα πρέπει να παραλειφθεί. Δεν είναι όλοι οι browsers τόσο αυστηροί, όμως μερικοί είναι. Με λίγα λόγια: απλά παράλειψε τη μονάδα στα μήκη.

Το πιο συνηθισμένο λάθος που μου έρχεται στο μυαλό όσον αφορά τους αριθμούς στη Sass, είναι το να πιστεύουμε ότι οι μονάδες είναι απλά κάποια strings που μπορούμε να προσαρτήσουμε με ασφάλεια σε έναν αριθμό. Ενώ ακούγεται σωστό, σίγουρα οι μονάδες δεν λειτουργούν κατ’ αυτόν τον τρόπο. Σκέψου τις μονάδες σαν αλγεβρικά σύμβολα. Για παράδειγμα, στον πραγματικό κόσμο, ο πολλαπλασιασμός 5 ίντσες επί 5 ίντσες μας δίνει 25 τετραγωνικές ίντσες. Η ίδια λογική εφαρμόζεται και στη Sass.

Για να προσθέσεις μια μονάδα σε έναν αριθμό, πρέπει να πολλαπλασιάσεις αυτόν τον αριθμό επί 1 μονάδα.

$value: 42;

// Yep
$length: $value * 1px;

// Nope
$length: $value + px;
$value: 42

// Yep
$length: $value * 1px

// Nope
$length: $value + px

Σημειώστε ότι το να προσθέσουμε 0 μέλη αυτής της μονάδας επίσης λειτουργεί, αλλά θα προτιμούσα να προτείνω την προαναφερθείσα μέθοδο επειδή η πρόσθεση 0 μονάδων μπορεί να μας μπερδέψει. Πράγματι, όταν προσπαθούμε να μετατρέψουμε έναν αριθμό σε μία άλλη συμβατή μονάδα, το να προσθέσουμε το 0 δεν έχει το αναμενόμενο αποτέλεσμα. Μπορείτε να διαβάσετε περισσότερα για αυτό το θέμα σε αυτό το άρθρο.

$value: 42 + 0px;
// -> 42px

$value: 1in + 0px;
// -> 1in

$value: 0px + 1in;
// -> 96px
$value: 42 + 0px
// -> 42px

$value: 1in + 0px
// -> 1in

$value: 0px + 1in
// -> 96px

Τελικά, στην πραγματικότητα εξαρτάται από το τι προσπαθείς να πετύχεις. Θυμήσου ότι το να προσθέσουμε τη μονάδα σαν string δεν είναι καλός τρόπος για να προχωρήσουμε.

Για να αφαιρέσεις τη μονάδα από μια τιμή, πρέπει να τη διαιρέσεις με μια μονάδα του είδους της.

$length: 42px;

// Yep
$value: $length / 1px;

// Nope
$value: str-slice($length + unquote(''), 1, 2);
$length: 42px

// Yep
$value: $length / 1px

// Nope
$value: str-slice($length + unquote(''), 1, 2)

Η προσάρτηση μιας μονάδας σαν string σε έναν αριθμό έχει σαν αποτέλεσμα string, που εμποδίζει περαιτέρω πράξεις σε αυτήν την τιμή. Ο διαχωρισμός του αριθμητικού μέρους ενός αριθμού με μια μονάδα επίσης έχει σαν αποτέλεσμα string. Αυτό είναι κάτι που δεν θέλεις. Use lengths, not strings.

Υπολογισμοί

Οι αριθμητικοί υπολογισμοί υψηλότερου επιπέδου πρέπει πάντα να βρίσκονται μέσα σε παρενθέσεις. Αυτή η απαίτηση όχι μόνο βελτιώνει δραματικά την αναγνωσιμότητα, αλλά προλαμβάνει και κάποιες ακραίες περιπτώσεις υποχρεώνοντας την Sass να χρησιμοποιήσει τις τιμές των περιεχομένων των παρενθέσεων.

// Yep
.foo {
  width: (100% / 3);
}

// Nope
.foo {
  width: 100% / 3;
}
// Yep
.foo
  width: (100% / 3)

// Nope
.foo
  width: 100% / 3

Μαγικοί αριθμοί

Ο “Μαγικός αριθμός” είναι ένας όρος της παλιάς σχολής προγραμματισμού για την unnamed αριθμητική σταθερά. Βασικά, είναι απλά ένας τυχαίος αριθμός ο οποίος συμβαίνει απλά να δουλεύει (just work™) ενώ δεν συνδέεται με καμία λογική εξήγηση.

Περιττό να πω ότι οι μαγικοί αριθμοί είναι κατάρα και πρέπει να αποφεύγονται πάση θυσία. Όταν δεν καταφέρνεις να βρεις μια λογική εξήγηση γιατί ένας αριθμός λειτουργεί, πρόσθεσε ένα εκτενές σχόλιο που να εξηγεί πώς έφτασες εκεί και γιατί πιστεύεις ότι λειτουργεί. Το να παραδεχτείς ότι δεν ξέρεις για ποιο λόγο λειτουργεί κάτι, είναι πιο βοηθητικό για τον επόμενο developer από το να τον αφήσεις να καταλάβει μόνος του από το μηδέν τι συμβαίνει.

/**
 * 1. Μαγικός αριθμός. Αυτή η τιμή είναι η μικρότερη που μπορούσα να βρω
 * για να ευθυγραμμίσω την κορυφή του `.foo` με τον γονέα του.
 * Ιδανικά, πρέπει να το διορθώσουμε κατάλληλα.
 */
.foo {
  top: 0.327em; /* 1 */
}
/**
 * 1. Μαγικός αριθμός. Αυτή η τιμή είναι η μικρότερη που μπορούσα να βρω
 * για να ευθυγραμμίσω την κορυφή του `.foo` με τον γονέα του.
 * Ιδανικά, πρέπει να το διορθώσουμε κατάλληλα.
 */
.foo
  top: 0.327em /* 1 */

Πάνω στο θέμα, το CSS-Tricks έχει ένα καταπληκτικό άρθρο σχετικά με τα magic numbers στη CSS το οποίο σας ενθαρρύνω να διαβάσετε.

Χρώματα

Τα χρώματα έχουν σημαντική θέση στη CSS. Φυσικά, η Sass καταλήγει να είναι ένας πολύτιμος σύμμαχος όσον αφορά τη χρήση χρωμάτων, κυρίως προσφέροντας πολλές δυνατές συναρτήσεις.

Η Sass είναι τόσο χρήσιμη σε ό,τι έχει να κάνει με την διαχείριση χρωμάτων που έχουν εμφανιστεί διάφορα άρθρα στο internet σχετικά με αυτό ακριβώς το θέμα. Επιτρέψτε μου να σας συστήσω μερικά:

Τύποι χρωμάτων

Για να κάνεις τα χρώματα όσο πιο απλά γίνεται, η συμβουλή μου είναι να τηρήσεις την ακόλουθη σειρά προτίμησης για τους τύπους χρωμάτων:

  1. Συμβολισμός HSL;
  2. Συμβολισμός RGB;
  3. Δεκαεξαδικός συμβολισμός (με μικρά γράμματα και συντομευμένος).

Δεν πρέπει να χρησιμοποιούνται οι λέξεις κλειδιά CSS χρωμάτων, εκτός αν πρόκεται για γρήγορα πρωτότυπα. Πράγματι, είναι αγγλικές λέξεις και μερικές από αυτές δεν περιγράφουν πολύ καλά το χρώμα που αντιπροσωπεύουν, ειδικά για κάποιους που η μητρική τους γλώσσα δεν είναι τα αγγλικά. Εκτός αυτού, οι λέξεις κλειδιά δεν είναι απόλυτα σωστές εννοιολογικά· για παράδειγμα το grey (γκρι) είναι στην πραγματικότητα πιο σκούρο από το darkgrey (σκούρο γκρι), και η σύγχυση μεταξύ του grey και του gray μπορεί να οδηγήσει σε αντιφατικές χρήσεις του χρώματος.

Ο συμβολισμός HSL είναι όχι μόνο ο πιο ευκατανόητος για το ανθρώπινο μυαλό, αλλά επίσης διευκολύνει τους συγγραφείς των stylesheet να αλλάξουν το χρώμα, επεμβαίνοντας στα hue (απόχρωση), saturation (κορεσμός), και lightness (φωτεινότητα) ξεχωριστά.

Το RGB έχει το πλεονέκτημα του να δείχνει αμέσως αν το χρώμα αποτελείται από περισσότερο μπλε, πράσινο ή κόκκινο. Συνεπώς μπορεί να είναι καλύτερο από το HSL σε ορισμένες περιπτώσεις, ειδικά όταν περιγράφουμε ένα καθαρό κόκκινο, πράσινο ή μπλε. Ωστόσο δεν είναι εύκολο να “χτίσουμε” ένα χρώμα από τα τρία μέρη.

Τέλος, το δεκαεξαδικό είναι σχεδόν ακατανόητο για το ανθρώπινο μυαλό. Χρησιμοποίησέ το μόνο σαν τελευταία λύση αν πρέπει.

// Yep
.foo {
  color: hsl(0, 100%, 50%);
}

// Also yep
.foo {
  color: rgb(255, 0, 0);
}

// Meh
.foo {
  color: #f00;
}

// Nope
.foo {
  color: #FF0000;
}

// Nope
.foo {
  color: red;
}
.foo
  color: hsl(0, 100%, 50%)

// Also yep
.foo
  color: rgb(255, 0, 0)

// Nope
.foo
  color: #f00

// Nope
.foo
  color: #FF0000

// Nope
.foo
  color: red

Όταν χρησιμοποιείς συμβολισμό HSL ή RGB, πάντα να προσθέτεις ένα κενό μετά το κόμμα (,) και καθόλου κενά μεταξύ των παρενθέσεων ((, )) και του περιεχομένου.

// Yep
.foo {
  color: rgba(0, 0, 0, 0.1);
  background: hsl(300, 100%, 100%);
}

// Nope
.foo {
  color: rgba(0,0,0,0.1);
  background: hsl( 300, 100%, 100% );
}
// Yep
.foo
  color: rgba(0, 0, 0, 0.1)
  background: hsl(300, 100%, 100%)

// Nope
.foo
  color: rgba(0,0,0,0.1)
  background: hsl( 300, 100%, 100% )

Χρώματα και μεταβλητές

Όταν χρησιμοποιείς ένα χρώμα πάνω από μια φορά, αποθήκευσέ το σε μια μεταβλητή με όνομα που να έχει νόημα και να αντιπροσωπεύει το χρώμα.

$sass-pink: hsl(330, 50%, 60%);
$sass-pink: hsl(330, 50%, 60%)

Τώρα είσαι ελεύθερος να χρησιμοποιήσεις τη μεταβλητή όποτε θέλεις. Παρόλα αυτά, αν η χρήση είναι στενά συνδεδεμένη με ένα θέμα, θα πρότεινα να μην χρησιμοποιείται η μεταβλητή όπως είναι. Αντ’ αυτού, αποθήκευσέ το σε μια άλλη μεταβλητή με όνομα που να εξηγεί πώς θα έπρεπε να χρησιμοποιείται.

$main-theme-color: $sass-pink;
$main-theme-color: $sass-pink

Με αυτό τον τρόπο προλαμβάνουμε μια αλλαγή θέματος που να οδηγεί σε κάτι σαν το $sass-pink: blue. Αυτό το άρθρο κάνει πολύ καλή δουλειά στο να εξηγήσει το πόσο σημαντικό είναι να δώσετε προσοχή στις μεταβλητές χρωμάτων.

Κάνοντας τα χρώματα πιο φωτεινά και πιο σκόυρα

Τόσο η συνάρτηση lighten όσο και η darken τροποποιούν την φωτεινότητα ενός χρώματος στο διάστημα HSL προσθέτοντας ή αφαιρώντας από τη φωτεινότητα σε αυτό το διάστημα. Βασικά, δεν είναι τίποτα παραπάνω από ψευδώνυμα για την παράμετρο $lightness της συνάρτησης adjust-color.

Το θέμα είναι ότι αυτές οι συναρτήσεις συχνά δεν παρέχουν το αναμενόμενο αποτέλεσμα. Από την άλλη η συνάρτηση mix είναι ένας ωραίος τρόπος για να κάνεις πιο φωτεινό ή πιο σκούρο ένα χρώμα αναμιγνύοντάς το είτε με το white (άσπρο) ή με το black (μαύρο).

Το πλεονέκτημα του να χρησιμοποιείς το mix αντί για κάποια από τις δύο προαναφερθείσες συναρτήσεις είναι ότι πηγαίνει προοδευτικά στο μαύρο (ή το άσπρο) καθώς μειώνεις το ποσοστό του χρώματος, ενώ το darken και το lighten υπερκαλύπτουν γρήγορα όλο το χρώμα με μαύρο ή άσπρο.

Εικονογράφηση των διαφορών μεταξύ lighten/darken και mix από την KatieK
Εικονογράφηση των διαφορών μεταξύ lighten/darken και mix από την KatieK

Αν δεν θέλεις να γράφεις τη συνάρτηση mix κάθε φορά, μπορείς να δημιουργήσεις δύο εύχρηστες συναρτήσεις, την tint και την shade (οι οποίες είναι επίσης μέρος του Compass) για να κάνεις το ίδιο πράγμα:

/// Φώτισε ελαφρά ένα χρώμα
/// @access public
/// @param {Color} $color - χρώμα το οποίο θα επεξεργαστούμε
/// @param {Number} $percentage - το ποσοστό του `$color` το οποίο θα περιέχεται μέσα στο χρώμα που θα επιστραφεί
/// @return {Color}
@function tint($color, $percentage) {
  @return mix(white, $color, $percentage);
}

/// Σκούρυνε ελαφρά ένα χρώμα
/// @access public
/// @param {Color} $color - χρώμα το οποίο θα σκουρύνει
/// @param {Number} $percentage - το ποσοστό του `$color` το οποίο θα περιέχεται μέσα στο χρώμα που θα επιστραφεί
/// @return {Color}
@function shade($color, $percentage) {
  @return mix(black, $color, $percentage);
}
/// Φώτισε ελαφρά ένα χρώμα
/// @access public
/// @param {Color} $color - χρώμα το οποίο θα επεξεργαστούμε
/// @param {Number} $percentage - το ποσοστό του `$color` το οποίο θα περιέχεται μέσα στο χρώμα που θα επιστραφεί
/// @return {Color}
@function tint($color, $percentage)
  @return mix($color, white, $percentage)

/// Σκούρυνε ελαφρά ένα χρώμα
/// @access public
/// @param {Color} $color - χρώμα το οποίο θα σκουρύνει
/// @param {Number} $percentage - το ποσοστό του `$color` το οποίο θα περιέχεται μέσα στο χρώμα που θα επιστραφεί
/// @return {Color}
@function shade($color, $percentage)
  @return mix($color, black, $percentage)

Η συνάρτηση scale-color είναι σχεδιασμένη έτσι ώστε να αυξομειώνει πιο ομαλά τα properties λαμβάνοντας υπόψη πόσο υψηλά η χαμηλά είναι ήδη. Παράγει αποτελέσματα που είναι το ίδιο όμορφα με τα αποτελέσματα της mix αλλά με πιο καθαρές κλήσεις. Ωστόσο ο συντελεστής προσαύξησης δεν είναι ακριβώς ο ίδιος.

Λίστες

Οι λίστες είναι οι πίνακες της Sass. Μια λίστα είναι μια επίπεδη δομή δεδομένων (σε αντίθεση με τα maps) που έχει σαν σκοπό να αποθηκεύει τιμές οποιουδήποτε τύπου (ακόμη και λίστες, που μας οδηγεί σε εμφωλευμένες λίστες).

Οι λίστες πρέπει να ακολουθούν τα εξής guidelines:

  • είτε σε μία σειρά, ή σε πολλές·
  • αναγκαστικά σε πολλές σειρές αν δεν χωράνε σε γραμμή 80 χαρακτήρων·
  • αν δεν χρησιμοποιούνται αυτούσιες για σκοπούς CSS, πάντα να διαχωρίζονται με κόμμα·
  • πάντα να περικλείονται από παρενθέσεις·
  • να έχουν κόμμα στο τέλος της γραμμής αν επεκτείνονται σε πολλές σειρές, να μην έχουν αν είναι στην ίδια σειρά.
// Yep
$font-stack: ('Helvetica', 'Arial', sans-serif);

// Yep
$font-stack: (
  'Helvetica',
  'Arial',
  sans-serif,
);

// Nope
$font-stack: 'Helvetica' 'Arial' sans-serif;

// Nope
$font-stack: 'Helvetica', 'Arial', sans-serif;

// Nope
$font-stack: ('Helvetica', 'Arial', sans-serif,);
// Yep
$font-stack: ('Helvetica', 'Arial', sans-serif)

// Nope (not supported)
$font-stack: (
  'Helvetica',
  'Arial',
  sans-serif,
)

// Nope
$font-stack: 'Helvetica' 'Arial' sans-serif

// Nope
$font-stack: 'Helvetica', 'Arial', sans-serif

// Nope
$font-stack: ('Helvetica', 'Arial', sans-serif,)

Όταν προσθέτεις νέα αντικείμενα σε μια λίστα, να χρησιμοποιείς πάντα το παρεχόμενο API. Μην δοκιμάζεις να προσθέσεις νέα αντικείμενα χειροκίνητα.

$shadows: (0 42px 13.37px hotpink);

// Yep
$shadows: append($shadows, $shadow, comma);

// Nope
$shadows: $shadows, $shadow;
$shadows: (0 42px 13.37px hotpink)

// Yep
$shadows: append($shadows, $shadow, comma)

// Nope
$shadows: $shadows, $shadow

Σε αυτό το άρθρο, σας προσφέρω αρκετές συμβουλές και κόλπα για το πώς να διαχειριστείτε σωστά lists στη Sass.

Maps

Με την Sass 3.3, οι συγγραφείς των stylesheet μπορούν να ορίσουν maps — τον όρο στη Sass για τους #fixme σχετιστικούς πίνακες, τα hash ή ακόμη και τα αντικείμενα Javascript. Το map είναι μια δομή δεδομένων που αντιστοιχίζει κλειδιά με τιμές. Και τα κλειδιά αλλά και οι τιμές μπορούν να είναι οποιουδήποτε τύπου δεδομένων, ακόμη και maps, αν και δεν θα συνιστούσα τη χρήση σύνθετων τύπων δεδομένων σαν κλειδιά ενός map, ας είμαστε λίγο λογικοί.

Τα maps πρέπει να είναι γραμμένα ως εξής:

  • κενό μετά την άνω και κάτω τελεία (:
  • άνοιγμα παρένθεσης (() στην ίδια γραμμή με την άνω και κάτω τελεία·
  • κλειδιά σε εισαγωγικά αν είναι strings (που αναπαριστά το 99% των περιπτώσεων)·
  • κάθε ζευγάρι κλειδιού/τιμής μόνο του σε νέα σειρά·
  • κόμμα (,) στο τέλος κάθε κλειδιού/τιμής·
  • κόμμα στο τέλος της σειράς (,) του τελευταίου αντικειμένου για να να είναι πιο εύκολο να προσθέσεις, να αφαιρέσεις ή να ανακατατάξεις τα αντικείμενα·
  • κλείσιμο παρένθεσης (,) σε νέα, δική της γραμμή·
  • όχι κενό ή νέα γραμμή μεταξύ της τελευταίας παρένθεσης ()) και του semi-colon (;).

Παράδειγμα:

// Yep
$breakpoints: (
  'small': 767px,
  'medium': 992px,
  'large': 1200px,
);

// Nope
$breakpoints: ( small: 767px, medium: 992px, large: 1200px );
// Yep
$breakpoints: ('small': 767px, 'medium': 992px, 'large': 1200px,)

// Nope
$breakpoints: ( 'small': 767px, 'medium': 992px, 'large': 1200px )

// Nope
$breakpoints: (small: 767px, medium: 992px, large: 1200px,)

// Nope (since it is not supported)
$breakpoints: (
  'small': 767px,
  'medium': 992px,
  'large': 1200px,
)

Τα αρθρα σχετικά με τα Sass maps είναι πολλά και προδίδουν πόσο πολυπόθητο ήταν αυτό το χαρακτηριστικό. Εδώ είναι 3 άρθρα που σας συστήνω να διαβάσετε: Using Sass Maps, Extra Map functions in Sass, Real Sass, Real Maps.

Σετ κανόνων CSS

Σε αυτό το σημείο, αυτό είναι πάνω κάτω μια επανάληψη όσων γνωρίζουν όλοι, αλλά ορίστε πώς πρέπει να γράφεται ένα σετ κανόνων CSS (τουλάχιστον σύμφωνα με τα περισσότερα guidelines, συμπεριλαμβανομένων και των CSS Guidelines):

  • σχετικοί selectors στην ίδια γραμμή· άσχετοι selectors σε νέες γραμμές·
  • η αγκύλη που ανοίγει τις δηλώσεις ({) να χωρίζεται από τον τελευταίο selector με ένα κενό·
  • κάθε δήλωση να είναι σε νέα, δική της γραμμή·
  • ένα κενό μετά την άνω και κάτω τελεία (:
  • ένα semi-colon (;) στο τέλος όλων των δηλώσεων·
  • η αγκύλη που κλείνει τις δηλώσεις (}) να είναι σε νέα, δική της γραμμή·
  • μία νέα γραμμή μετά την τελευταία αγκύλη }.

Παράδειγμα:

// Yep
.foo, .foo-bar,
.baz {
  display: block;
  overflow: hidden;
  margin: 0 auto;
}

// Nope
.foo,
.foo-bar, .baz {
    display: block;
    overflow: hidden;
    margin: 0 auto }
// Yep
.foo, .foo-bar,
.baz
  display: block
  overflow: hidden
  margin: 0 auto

// Nope
.foo,
.foo-bar, .baz
    display: block
    overflow: hidden
    margin: 0 auto

Επιπρόσθετα με αυτά τα guidelines που είναι σχετικά με CSS, θέλουμε να δώσουμε προσοχή και στα παρακάτω:

  • τοπικές μεταβλητές που δηλώνονται πριν από όλες τις δηλώσεις, και χωρίζονται από τις δηλώσεις με μια νέα γραμμή·
  • κλήσεις σε mixin που δεν έχουν @content και εμφανίζονται πριν από όλες τις δηλώσεις·
  • στους εμφωλευμένους selectors που εμφανίζονται πάντα μετά από νέα γραμμή·
  • κλήσεις σε mixin που έχουν @content και εμφανίζονται μετά από εμφωλευμένο selector·
  • όχι νέα γραμμή πριν την τελευταία αγκύλη (}).

Παράδειγμα:

.foo, .foo-bar,
.baz {
  $length: 42em;

  @include ellipsis;
  @include size($length);
  display: block;
  overflow: hidden;
  margin: 0 auto;

  &:hover {
    color: red;
  }

  @include respond-to('small') {
    overflow: visible;
  }
}
.foo, .foo-bar,
.baz
  $length: 42em

  +ellipsis
  +size($length)
  display: block
  overflow: hidden
  margin: 0 auto

  &:hover
    color: red

  +respond-to('small')
    overflow: visible

Ταξινόμηση των δηλώσεων

Δεν μπορώ να σκεφτώ πολλά θέματα όπου οι απόψεις διίστανται τόσο όσο αυτές που αφορούν την ταξινόμηση των δηλώσεων στη CSS. Συγκεκριμένα, υπάρχουν δύο παρατάξεις εδώ:

  • παραμονή στην αλβαφητική ταξινόμηση·
  • ταξινόμηση δηλώσεων κατά τύπο (position, display, colors, font, διάφορα…).

Υπάρχουν πλεονεκτήματα και μειονεκτήματα και στους δύο τρόπους. Από τη μία, η αλφαβητική ταξινόμηση είναι καθολική (τουλάχιστον για τις γλώσσες που χρησιμοποιούν το λατινικό αλφάβητο) και έτσι δεν υπάρχει επιχείρημα για την ταξινόμηση ενός property πριν από ένα άλλο. Παρόλα αυτά μου φαίνεται εξαιρετικά περίεργο να βλέπω properties όπως τα bottom και top να μην είναι ακριβώς δίπλα το ένα στο άλλο. Γιατί θα ‘πρεπε τα animations να εμφανίζονται πριν το display type; Υπάρχουν πολλές παραξενιές με την αλφαβητική ταξινόμηση.

.foo {
  background: black;
  bottom: 0;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
  height: 100px;
  overflow: hidden;
  position: absolute;
  right: 0;
  width: 100px;
}
.foo
  background: black
  bottom: 0
  color: white
  font-weight: bold
  font-size: 1.5em
  height: 100px
  overflow: hidden
  position: absolute
  right: 0
  width: 100px

Από την άλλη, η ταξινόμηση των properties κατά τύπο βγάζει πολύ νόημα. Όλες οι δηλώσεις σχετικές με fonts είναι συγκεντρωμένες, τα top και bottom επανενώνονται και το διάβασμα ενός ruleset μοιάζει κάπως με το διάβασμα μιας μικρής ιστορίας. Αλλά εκτός αν παραμείνεις σε ορισμένες συμβάσεις όπως το Idiomatic CSS, υπάρχουν πολλά περιθώρια ερμηνείας σε αυτό τον τρόπο δράσης. Πού θα πήγαινε το white-space: στο font ή στο display; Πού ακριβώς ανήκει το overflow; Ποια είναι η σειρά των properties μέσα σε ένα γκρουπ (θα μπορούσε να είναι αλφαβητική, τι ειρωνία);

.foo {
  height: 100px;
  width: 100px;
  overflow: hidden;
  position: absolute;
  bottom: 0;
  right: 0;
  background: black;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}
.foo
  height: 100px
  width: 100px
  overflow: hidden
  position: absolute
  bottom: 0
  right: 0
  background: black
  color: white
  font-weight: bold
  font-size: 1.5em

Υπάρχει επίσης ακόμη ένα ενδιαφέρον υποδένδρο ταξινόμησης κατά τύπο που λέγεται Ομόκεντρη (Concentric) CSS και φαίνεται να είναι επίσης αρκετά δημοφιλές. Βασικά, η Ομόκεντρη CSS βασίζεται στο box-model για να για να ορίσει μια σειρά: ξεκινάει από έξω και προχωράει προς τα μέσα.

.foo {
  width: 100px;
  height: 100px;
  position: absolute;
  right: 0;
  bottom: 0;
  background: black;
  overflow: hidden;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}
.foo
  width: 100px
  height: 100px
  position: absolute
  right: 0
  bottom: 0
  background: black
  overflow: hidden
  color: white
  font-weight: bold
  font-size: 1.5em

Πρέπει να πω ότι ούτε εγώ δεν μπορώ να αποφασίσω. Μία πρόσφατη δημοσκόπηση στο CSS-Tricks είχε ως αποτέλεσμα ότι πάνω από το 45% των developers ταξινομεί τις δηλώσεις κατά τύπο ενώ το 14% ταξινομεί αλφαβητικά. Ακόμη, υπάρχει κι ένα 39% που τις ταξινομεί τελείως τυχαία, συμπεριλαμβανομένου και εμού.

Διάγραμμα που δείχνει πως οι developers ταξινομούν τα CSS declarations
Διάγραμμα που δείχνει πως οι developers ταξινομούν τα CSS declarations

Εξαιτίας αυτού, δεν θα επιβάλω κάποια επιλογή σε αυτό το styleguide. Διάλεξε όποια προτιμάς, αρκεί να είσαι σταθερός σε όλα τα stylesheets σου (πχ. όχι την τυχαία επιλογή).

Μια πρόσφατη έρευνα έδειξε ότι η χρήση του CSS Comb (το οποίο χρησιμοποιεί ταξινόμηση κατά τύπο) για την ταξινόμηση των δηλώσεων CSS, καταλήγει στη μείωση του μέσου μεγέθους του αρχείου με συμπίεση Gzip κατά 2.7%, έναντι του 1.3% της ταξινόμησης με αλφαβητική σειρά.

Εμφώλευση Selectors

Ένα συγκεκριμένο χαρακτηριστικό που προσφέρει η Sass στο οποίο γίνεται υπερβολική κατάχρηση από τους developers είναι η εμφώλευση των selectors. Η εμφώλευση των selectors προσφέρει έναν τρόπο στους συγγραφείς stylesheet για να υπολογίσουν τους μεγάλους selectors εμφωλεύοντας μικρούς selectors μέσα σε άλλους.

Γενικός κανόνας

Για παράδειγμα, η ακόλουθη εμφώλευση Sass:

.foo {
  .bar {
    &:hover {
      color: red;
    }
  }
}
.foo
  .bar
    &:hover
      color: red

… παράγει αυτή τη CSS:

.foo .bar:hover {
  color: red;
}

Παρομοίως, από την Sass 3.3 και μετά είναι δυνατόν να χρησιμοποιήσεις την αναφορά στον τρέχοντα selector (&) για να παράγεις εξειδικευμένους selectors. Για παράδειγμα:

.foo {
  &-bar {
    color: red;
  }
}
.foo
  &-bar
    color: red

… παράγει αυτή τη CSS:

.foo-bar {
  color: red;
}
.foo-bar
  color: red

Αυτή η μέθοδος χρησιμοποιείται συχνά μαζί με τις Συνθήκες ονομασίας BEM για να παράγει selectors .block__element και .block--modifier βασισμένους στον αρχικό selector (πχ. .block σε αυτή την περίπτωση).

Μπορεί να είναι ανεπίσημο, αλλά η παραγωγή νέων selectors με αναφορά στον τρέχοντα selector (&) καθιστά αδύνατη την αναζήτηση αυτών των selectors στην codebase αφού δεν υπάρχουν καθ’ εαυτοί.

Το πρόβλημα με την εμφώλευση των selectors είναι ότι τελικά κάνει τον κώδικα πιο δύσκολο στην ανάγνωση. Πρέπει κανείς να υπολογίσει νοητά τον selector που παράγεται από τα επίπεδα εμφώλευσης· δεν είναι πάντα αρκετά εμφανές τι θα καταλήξει να είναι η CSS.

Αυτή η δήλωση αληθεύει όλο και περισσότερο όσο οι selectors γίνονται μακρύτεροι και οι αναφορές στον τρέχοντα selector (&) όλο και συχνότερες. Σε ένα σημείο, ο κίνδυνος να μην μπορείς να παρακολουθήσεις και να μην καταλαβαίνεις πια τι συμβαίνει είναι τόσο υψηλός που δεν αξίζει τον κόπο.

Για να αποφύγουμε μια τέτοια κατάσταση, αποφεύγουμε την εμφώλευση των selectors όσο το δυνατόν περισσότερο. Παρόλα αυτά, προφανώς υπάρχουν μερικές εξαιρέσεις σε αυτόν τον κανόνα.

Για να αποφύγουμε μια τέτοια κατάσταση, συνηθίζαμε να μιλάμε για το Inception rule πριν απο μερικά χρόνια. Αυτό συμβούλευε κατά της εμφώλευσης πέραν των 3 επιπέδων, έχοντας ως σημείο αναφοράς την ταινία Inception του Christopher Nolan. Θα ήθελα να γίνω λίγο πιο δραστικός και να προτείνω την αποφυγή εμφωλευμένων selectors όσο το δυνατόν περισσότερο.

Ενώ υπάρχουν πολλές προφανείς εξαιρέσεις στον κανόνα όπως θα δούμε στο επόμενο τμήμα, αυτή η άποψη φαίνεται να είναι αρκετά δημοφιλής. Μπορείτε να διαβάσετε γι’ αυτό με περισσότερες λεπτομέρειες στο Beware of Selector Nesting και στο Avoid nested selectors for more modular CSS.

Εξαιρέσεις

Για αρχή, επιτρέπεται και ακόμη προτείνεται να εμφωλεύετε τα pseudo-classes και pseudo-elements μέσα στον αρχικό selector.

.foo {
  color: red;

  &:hover {
    color: green;
  }

  &::before {
    content: 'pseudo-element';
  }
}
.foo
  color: red

  &:hover
    color: green

  &::before
    content: 'pseudo-element'

Η χρήση εμφώλευσης των selectors για pseudo-classes και pseudo-elements όχι μόνο βγάζει νόημα (επειδή έχει να κάνει με στενά συνδεδεμένους selectors), αλλά και επειδή βοηθάει στο να κρατήσετε τα πάντα σχετικά με ένα component στο ίδιο σημείο.

Ακόμη, όταν χρησιμοποιείς classes κατάστασης όπως το .is-active που δεν έχουν να κάνουν με ένα μόνο component, είναι απολύτως εντάξει να το εμφωλεύσετε κάτω από τον selector του component για να κρατήσετε τα πράγματα τακτοποιημένα.

.foo {
  // …

  &.is-active {
    font-weight: bold;
  }
}
.foo
  // …

  &.is-active
    font-weight: bold

Τελευταίο αλλά εξίσου σημαντικό, όταν γράφετε τα styles για ένα element επειδή τυχαίνει να εμπεριέχεται σε ένα άλλο συγκεκριμένο element, είναι επίσης εντάξει να χρησιμοποιήσετε εμφώλευση για να κρατήσετε τα πάντα σχετικά με αυτό το component στο ίδιο σημείο.

.foo {
  // …

  .no-opacity & {
    display: none;
  }
}
.foo
  // …

  .no-opacity &
    display: none

Όπως με όλα, οι λεπτομέρειες είναι κάπως άσχετες, η συνέπεια είναι το κλειδί. Αν αισθάνεστε εντελώς σίγουροι για την εμφώλευση των selectors, τότε χρησιμοποιήστέ την. Απλά σιγουρευτείτε ότι όλη σου η ομάδα είναι εντάξει με αυτό.

Συμβάσεις ονομασίας

Σ’ αυτήν την ενότητα δε θα ασχοληθούμε με τις καλύτερες συμβάσεις ονομασίας στη CSS για συντηρησιμότητα (maintainability) και κλιμάκωση (scale); όχι μόνο γιατί είναι στην κρίση σου, αλλά είναι εκτός του πεδιού εφαρμογής ενός Sass styleguide. Προτείνω αυτά που συνιστώνται στα CSS Guidelines.

Υπάρχουν κάποια πράγματα που μπορείς να ονομάσεις στη Sass, και είναι σημαντικό να τα ονομάσεις σωστά ώστε όλος ο κώδικας να φαίνεται τόσο συνεπής όσο και ευανάγνωστος:

  • μεταβλητές·
  • συναρτήσεις·
  • mixins.

Οι Sass placeholders παραλείπονται σκόπιμα από αυτή τη λίστα αφού μπορούν να θεωρηθούν κανονικοί CSS selectors, εκ τούτου ακολουθούν το ίδιο μοτίβο ονοματοδοσίας όπως οι κλάσεις.

Όσον αφορά τις μεταβλητές, τις συναρτήσεις και τα mixins, τηρούμε κάτι πολύ CSS-ικό: πεζά με-παύλες, και κυρίως να βγάζουν νόημα.

$vertical-rhythm-baseline: 1.5rem;

@mixin size($width, $height: $width) {
  // …
}

@function opposite-direction($direction) {
  // …
}
$vertical-rhythm-baseline: 1.5rem

=size($width, $height: $width)
  // …

@function opposite-direction($direction)
  // …

Αν πραγματικά θέλετε να παίξετε με τις ιδέες των σταθερών στη Sass, θα πρέπει να διαβάσετε αυτό το ειδικό άρθρο.

Σταθερές

Αν τυγχάνει να είσαι framework developer ή συγγραφέας μιας βιβλιοθήκης, ενδέχεται να αντιμετωπίσεις κάποιες μεταβλητές που δεν πρέπει να αλλάξουν σε καμία περίπτωση: οι σταθερές. Δυστυχώς (ή ευτυχώς;), η Sass δεν παρέχει κάποιον τρόπο για να ορίσουμε τέτοιες οντότητες, οπότε πρέπει να τηρήσουμε αυστηρές συμβάσεις όσον αφορά την ονομασία για να τις επισημάνουμε.

Όπως σε πολλές γλώσσες, προτείνω μεταβλητές με κεφαλαία και κάτω παύλες όταν είναι σταθερές. Όχι μόνο είναι μία πολύ παλιά σύμβαση, αλλά δημιουργεί καλή αντίθεση με τις μεταβλητές που είναι με πεζά και παύλες.

// Yep
$CSS_POSITIONS: (top, right, bottom, left, center);

// Nope
$css-positions: (top, right, bottom, left, center);
// Yep
$CSS_POSITIONS: (top, right, bottom, left, center)

// Nope
$css-positions: (top, right, bottom, left, center)

Περαιτέρω ανάγνωση:

Namespace

Αν σκοπεύεις να διανείμεις τον κώδικά σου, στην περίπτωση μιας βιβλιοθήκης, ενός framework, ενός grid system ή οτιδήποτε, μπορεί να θες να εξετάσεις το ενδεχόμενο να χρησιμοποιήσεις ένα namespace για όλες τις μεταβλητές, τις συναρτήσεις, τα mixins και τους placeholders ώστε να μην συγκρουστούν με τον κώδικα αλλωνών.

Για παράδειγμα, αν δουλεύεις σε ένα project που λέγεται Sassy Unicorn που προορίζεται να είναι κατανεμημένο, θα μπορούσες να χρησιμοποιήσεις το su- ως namespace. Είναι αρκετά συγκεκριμένο ώστε να αποκλειστεί οποιαδήποτε σύγκρουση ονομάτων και αρκετά σύντομο ώστε να μην είναι κουραστικό να το γράφεις.

$su-configuration: (  );

@function su-rainbow($unicorn) {
  // …
}
$su-configuration: (  )

@function su-rainbow($unicorn)
  // …

Το Kaelig έχει ένα πολύ διορατικό άρθρο σχετικά με το global CSS namespace, σε περίπτωση που αυτό το θέμα σας ενδιαφέρει.

Σημείωσε ότι το αυτόματο namespacing είναι οπωσδήποτε ένας σχεδιαστικός στόχος για την ανερχόμενη ανανέωση του @import στη Sass 4.0. Δεδομένου ότι πλησιάζουμε στην κυκλοφορία, θα γίνεται όλο και λιγότερο χρήσιμο το χειροκίνητο namespacing· ενδεχομένως οι βιβλιοθήκες με μη αυτόματο namespacing να είναι δυσκολότερες στη χρήση.

Σχόλια

Η CSS είναι ζόρικη γλωσσα, γεμάτη με hacks και παραξενιές. Εξαιτίας αυτού, θα πρέπει να περιέχει αρκετά σχόλια, ειδικά αν εσύ ή κάποιος άλλος έχει σκοπό να διαβάσει τον κώδικα σε μισό με ένα χρόνο από τώρα. Μην αφήσεις τον εαυτό σου ή κάποιον άλλον να έρθει σε αυτή τη θέση: δεν-υπάρχει-περίπτωση-να-το-έγραψα-εγώ-αυτό-για-τον-θεόοοο.

Δεν έχει σημασία ποσο απλή απλή μπορεί να μοιάζει η CSS, υπάρχει πάντα χώρος για σχόλια. Αυτά θα μπορούσαν να εξηγήσουν:

  • την δομή ή και τον ρόλο του αρχείου·
  • το στόχο του ruleset·
  • την ιδέα πίσω από ένα magic number·
  • τον λόγο για μια δήλωση CSS·
  • την σειρά των δηλώσεων CSS·
  • τη διαδικασία σκέψης πίσω από έναν τρόπο αντιμετώπισης των πραγμάτων.

Σίγουρα έχω ξεχάσει να αναφέρω και άλλες περιπτώσεις. Τα σχόλια απαιτούν πολύ λίγο χρόνο αν τα γράψεις την ώρα που γράφεις τον κώδικα, γι’αυτό καλό θα ήταν να μην διστάσεις να τα γράψεις την εκείνη τη στιγμή. Το να επιστρέψεις μετά για να γράψεις σχόλια δεν είναι μονο εξωπραγματικό αλλα και υπερβολικά ενοχλητικό.

Γράφοντας Σχόλια

Ιδανικά, κάθε CSS ruleset πρέπει να προηγείται απο ενα ένα C-style σχόλιο εξηγώντας το νόημα του CSS μπλοκ. Αυτό το σχόλιο επίσης φιλοξενεί αριθμημένες διευκρινίσεις σχετικά με συγκεκριμένων τμημάτων του ruleset. Για παράδειγμα:

/**
 * Βοηθητική κλάση για να περικόψει και να προσθέσει αποσιωπητικά σε ένα string το οποίο είναι πάρα πολύ μακρύ για να χωρέσει
 * σε μία μόνο γραμμή.
 * 1. Αποτρέπει το περιεχόμενο απο το να αναδίπλωνει, αναγκάζοντας το να εμφανιστεί σε μία μόνο γραμμή.
 * 2. Προσθέτει αποσιωπητικά στο τέλος της γραμμής.
 */
.ellipsis {
  white-space: nowrap; /* 1 */
  text-overflow: ellipsis; /* 2 */
  overflow: hidden;
}
/**
* Βοηθητική κλάση για να περικόψει και να προσθέσει αποσιωπητικά σε ένα string το οποίο είναι πάρα πολύ μακρύ για να χωρέσει
* σε μία μόνο γραμμή.
* 1. Αποτρέπει το περιεχόμενο απο το να αναδίπλωνει, αναγκάζοντας το να εμφανιστεί σε μία μόνο γραμμή.
* 2. Προσθέτει αποσιωπητικά στο τέλος της γραμμής.
 */
.ellipsis
  white-space: nowrap /* 1 */
  text-overflow: ellipsis /* 2 */
  overflow: hidden

Πρακτικά οτιδήποτε δεν είναι προφανές με την πρώτη ματιά πρέπει να έχει σχόλια. Υπάρχει πάντα χώρος για παραπάνω documentation. Να θυμάστε πως ποτέ δεν θα έχετε σχολιάσει αρκετά, γι αυτό γράψτε όσα περισσότερα σχόλια μπορείτε για οτιδήποτε θεωρείται σημαντικό.

Όταν σχολιάζετε ένα τμήμα σχετικό με την Sass, χρησιμοποιείστε inline Sass σχόλια αντί για σχόλια C-style block ετσι ώστε αυτά να μην εμφανιστούν στο τελικό αποτέλεσμα, ακόμα και σε λειτουργία expanded κατα την διάρκεια ανάπτυξης του κώδικα.

// Πρόσθεσε το τρέχον module στην λίστα με τα imported modules.
// Το `!global` flag απαιτείται για να μπορεί να ανανεώνει την global μεταβλητή.
$imported-modules: append($imported-modules, $module) !global;
// Πρόσθεσε το τρέχον module στην λίστα με τα imported modules.
// Το `!global` flag απαιτείται για να μπορεί να ανανεώνει την global μεταβλητή.
$imported-modules: append($imported-modules, $module) !global

Σημείωση ότι αυτός ο τρόπος αντιμετώπισης των πραγμάτων υποστηρίζεται επίσης απο τα CSS Guidelines στο Commenting κεφάλαιο.

Documentation

Κάθε μεταβλητή, συνάρτηση, mixin και placeholder τα οποία έχουν ως σκοπό να ξαναχρησιμοποιηθούν παντού στο code base σας πρέπει να έχουν documentation στο πλαίσιο του global API χρησιμοποιώντας το SassDoc.

/// Η γραμμή βάσης του κάθετου ρυθμού που χρησιμοποιείται στο σύνολο του κώδικα.
/// @type Length
$vertical-rhythm-baseline: 1.5rem;
/// Η γραμμή βάσης του κάθετου ρυθμού που χρησιμοποιείται στο σύνολο του κώδικα.
/// @type Length
$vertical-rhythm-baseline: 1.5rem

Απαιτούνται τρία slashes (/).

Το SassDoc έχει δύο σημαντικούς ρόλους:

  • Να αναγκάζει την σύνταξη τυποποιημένων σχολίων χρησιμοποιώντας ένα σύστημα βασισμένο στα annotations για οτιδήποτε έχει να κάνει με το public ή private API;
  • το να μπορεί να παράγει μια έκδοση HTML του API documentation χρησιμοποιώντας οποιοδήποτε απο τα εργαλεία του SassDoc (CLI tool, Grunt, Gulp, Broccoli, Node…).
Το documentation δημιουργήθηκε με SassDoc
Το documentation δημιουργήθηκε με SassDoc

Παρακάτω μπορείτε να δείτε ένα παράδειγμα ενός mixin το οποίο είναι εκτενώς documented με το SassDoc:

/// Mixin το οποίο βοηθάει στον ορισμό του `width` και του `height` ταυτόχρονα.
///
/// @author Kitty Giraudel
///
/// @access public
///
/// @param {Length} $width - Element’s `width`
/// @param {Length} $height [$width] - Element’s `height`
///
/// @example scss - Usage
///   .foo {
///     @include size(10em);
///   }
///
///   .bar {
///     @include size(100%, 10em);
///   }
///
/// @example css - CSS output
///   .foo {
///     width: 10em;
///     height: 10em;
///   }
///
///   .bar {
///     width: 100%;
///     height: 10em;
///   }
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}
/// Mixin το οποίο βοηθάει στον ορισμό του `width` και του `height` ταυτόχρονα.
///
/// @author Kitty Giraudel
///
/// @access public
///
/// @param {Length} $width - Element's `width`
/// @param {Length} $height ($width) - Element's `height`
///
/// @example scss - Usage
///   .foo
///     +size(10em)
///
///   .bar
///     +size(100%, 10em)
///
/// @example css - CSS output
///   .foo {
///     width: 10em;
///     height: 10em;
///   }
///
///   .bar {
///     width: 100%;
///     height: 10em;
///   }
=size($width, $height: $width)
  width: $width
  height: $height

Αρχιτεκτονική

Η δόμηση ενός CSS project είναι ίσως ένα απο τα δυσκολότερα πράγματα που θα κάνετε κατα την διαρκεια ζωής του project. Το να συντηρήσετε την δόμηση αυτή συνεπή και ουσιώδης είναι ακόμα δυσκολότερο.

Ευτυχώς, ένα απο τα βασικά πλεονεκτήματα της χρήσης ενός CSS preprocessor είναι η παροχή της δυνατότητας τμηματοποίησης του κώδικα σε πολλά αρχεία χωρίς να επηρεάζεται η απόδοση του (όπως με το @import CSS directive). Λόγο της υπερφόρτωσης που κάνει η Sass στο @import directive, είναι απολύτως ασφαλή (και συνιστάται ανεπιφύλακτα) η χρήση όσο αρχείων χρειάζεται κατα την διάρκεια της ανάπτυξης, αφού όλα τα αρχεία θα συγκεντρωθούν σε ένα αρχείο CSS σε production περιβάλλον.

Πέρα από αυτό, δεν μπορώ να τονίσω αρκετά την ανάγκη για χρήση φακέλων, ακόμα και σε μικρά projects. Στο σπίτι, δεν πετάς όλα τα χαρτιά στο ίδιο κουτί. Χρησιμοποιείς φακέλους, εναν για το σπίτι/διαμέρισμα, έναν για την τράπεζα, έναν για τους λογαριασμούς και τα λοιπά. Δεν υπάρχει λόγος να γίνει διαφορετικά και για την δόμηση του CSS project σας. Διαχωρίστε τον κώδικα σε διαφορετικούς φακέλους, ονομασμένους λογικά, έτσι ώστε αργότερα να σας είναι εύκολο να βρείτε αυτό που ψάχνετε εύκολα και γρήγορα.

Υπάρχουν πολλές δημοφιλείς αρχιτεκτονικές για CSS project(s) όπως: OOCSS, Atomic Design, projects βασισμένα σε Bootstrap ή σε Foundation… Τα οποία έχουν πλεονεκτήματα και μειονεκτήματα.

Εγώ, προσωπικά, χρησιμοποιώ μια προσέγγιση που μοιάζει πολύ με αυτή του SMACSS του Jonathan Snook, η οποία επικεντρώνεται στην διατήρηση της κατάστασης όσο πιο απλής και φανερής γίνεται.

Έχω μάθει πως η δόμηση του κώδικα τις περισσότερες φορές αλλάζει ανάλογα με το project. Μη διστάσετε να απορρίψετε εντελώς ή να προσαρμόσετε την προτεινόμενη λύση έτσι ώστε να φτάσετε σε ένα αποτέλεσμα που θα ταιριάζει στις ανάγκες σας.

Components

Υπάρχει μια τεράστια διαφορά μεταξύ του να κάνεις κάτι να δουλεύει και να κάνει κάτι καλά. Πάλι, η CSS είναι πολύ «ακατάστατη» γλώσσα [citation needed]. Όσο λιγότερη CSS έχουμε, τόσο το καλύτερο. Δεν θέλουμε να αντιμετωπίσουμε καταστάσεις στις οποίες θα έχουμε megabytes απο κώδικα CSS. Για να κρατήσουμε τα stylesheets μικρά και αποδοτικά — και προφανώς δεν θα σας ξαφνιάσει — είναι καλό να σκεφτόμαστε το interface ως μια συλλογή απο components.

Τα Components μπορεί να είναι οτιδήποτε, αρκεί:

  • να κάνουν μόνο ένα πράγμα·
  • να είναι επαναχρησιμοποιήσιμα και να επαναχρησιμοποιούνται μέσα στο project·
  • να είναι ανεξάρτητα.

Για παράδειγμα, μια φόρμα αναζήτησης θα πρέπει να θεωρείται ως ένα component. Πρέπει να είναι επαναχρησιμοποιήσιμο, σε διαφορετικές τοποθεσίες, σε διαφορετικές σελίδες, σε διαφορετικές καταστάσεις. Δεν πρέπει να εξαρτάται απο την θέση του στο DOM (footer, sidebar, κύριο περιεχόμενο…).

Σχεδόν όλα τα interface μπορούμε να τα θεωρήσουμε ως components και είναι αυτό που σας συνιστώ. Αυτό θα μικρύνει κατά πολύ την ποσότητα CSS κώδικα που χρειάζεται το project, αλλά τυγχάνει επίσης να είναι και ευκολότερο στην συντήρηση από ένα χάος όπου όλα είναι ακατάστατα.

Δομή Component

Ιδανικά, τα components πρέπει να βρίσκονται μέσα σε δικά τους Sass partial (μέσα στον φάκελο components/, όπως περιγράφεται στο 7-1 pattern), π.χ. components/_button.scss. Τα styles που περιγράφονται μέσα σε κάθε αρχείο component πρέπει να έχουν να κάνουν μόνο με:

  • Το style του ίδιου του component·
  • Το style παραλλαγών, τροποποιητών ή/και καταστάσεων του component·
  • Τα styles των απογόνων του component (λ.χ. children), εάν είναι απαραίτητο.

Αν θέλετε τα components σας να παίρνουν το θέμα τους απο εξωτερικούς παράγοντες (π.χ. απο ένα θέμα μέσα στο φάκελο themes/), περιορίστε τις δηλώσεις σας σε δομικά styles, όπως είναι οι διαστάσεις (width/height), padding, margins, alignment, και τα λοιπά. Αποκλείστε styles όπως τα χρώματα, σκιές, font rules, background rules, και τα λοιπά.

Ένα component partial μπορεί να περιέχει παραλλαγές σχετικές με το component, placeholders, ακόμα και mixins ή functions. Κρατήστε στο μυαλό σας, όμως, ότι πρέπει να αποφύγετε το referencing (π.χ. να κάνετε @import) component αρχεία μέσα απο άλλα αρχεία component, γιατί αυτό μπορεί να κάνει το dependency graph του project σας αδύνατο να συντηρηθεί.

Εδώ είναι ένα παράδειγμα component partial ενός button:

// Button-specific variables
$button-color: $secondary-color;

// … include any button-specific:
// - mixins
// - placeholders
// - functions

/**
 * Buttons
 */
.button {
  @include vertical-rhythm;
  display: block;
  padding: 1rem;
  color: $button-color;
  // … etc.

  /**
   * Inlined buttons on large screens
   */
  @include respond-to('medium') {
    display: inline-block;
  }
}

/**
 * Icons within buttons
 */
.button > svg {
  fill: currentcolor;
  // … etc.
}

/**
 * Inline button
 */
.button--inline {
  display: inline-block;
}
// Button-specific variables
$button-color: $secondary-color

// ... include any button-specific:
// - mixins
// - placeholders
// - functions

/**
 * Buttons
 */
.button
  +vertical-rhythm
  display: block
  padding: 1rem
  color: $button-color
  // ... etc.

  /**
   * Inlined buttons on large screens
   */
  +respond-to('medium')
    display: inline-block
}

/**
 * Icons within buttons
 */
.button > svg
  fill: currentcolor
  // ... etc.

/**
 * Inline button
 */
.button--inline
  display: inline-block

Ευχαριστώ τον David Khourshid για τη βοήθεια και την εμπειρογνωμοσύνη που προσέφερε σε αυτό το τμήμα.

Το 7-1 pattern

Επιστρέφουμε στην αρχιτεκτονική. Συνηθίζω να ακολουθώ το 7-1 pattern: 7 φακέλους, 1 αρχείο. Ουσιαστικά, έχεις όλα τα partials μέσα σε 7 διαφορετικούς φακέλους, και ένα αρχείο στο πρώτο επίπεδο (συνήθως με την ονομασία main.scss) το οποίο κάνει import όλα τα άλλα για να τα κάνει compile σε ένα CSS stylesheet.

  • base/
  • components/
  • layout/
  • pages/
  • themes/
  • abstracts/
  • vendors/

Και φυσικά:

  • main.scss

Αν σας ενδιαφέρει να χρησιμοποιήσετε το 7-1 pattern, υπάρχει ήδη το boilerplate έτοιμο στο GitHub. Πρέπει να περιέχει όλα όσα χρειάζεστε για να ξεκινήσετε με αυτή την αρχιτεκτονική.

Wallpaper από τον Julien He
Wallpaper από τον Julien He

Ιδανικά, μπορούμε να καταλήξουμε σε κάτι σαν αυτό:

sass/
|
| abstracts/
|   | _variables.scss    # Μεταβλητές Sass
|   | _functions.scss    # Συναρτήσεις Sass
|   | _mixins.scss       # Sass Mixins
|   | _placeholders.scss # Sass placeholders
|
| base/
|   | _reset.scss        # Reset/normalize
|   | _typography.scss   # Κανόνες τυπογραφίας
|                        # Κλπ.
|
| components/
|   | _buttons.scss      # Κουμπιά
|   | _carousel.scss     # Carousel
|   | _cover.scss        # Cover
|   | _dropdown.scss     # Dropdown
|                        # Κλπ.
|
| layout/
|   | _navigation.scss   # Μενού πλοήγησης
|   | _grid.scss         # Πλέγμα
|   | _header.scss       # Header
|   | _footer.scss       # Footer
|   | _sidebar.scss      # Sidebar
|   | _forms.scss        # Φόρμες
|                        # Κλπ.
|
| pages/
|   | _home.scss         # Styles αρχικής σελίδας
|   | _contact.scss      # Styles φόρμας επικοινωνίας
|                        # Κλπ.
|
| themes/
|   | _theme.scss        # Κύριο theme
|   | _admin.scss        # Διαχειριστικό theme
|                        # Κλπ.
|
| vendors/
|   | _bootstrap.scss    # Bootstrap
|   | _jquery-ui.scss    # jQuery UI
|                        # Κλπ.
|
`– main.scss              # Κύριο αρχείο Sass

Τα αρχεία ακολουθούν την ίδια ονοματική σύμβαση που περιγράψαμε παραπάνω: είναι οριοθετημένα με κάτω παύλα.

Φάκελος Base

O φάκελος base/ περιέχει αυτό που αποκαλούμε κώδικα boilerplate για το project. Εκεί μπορεί να βρεθεί το reset αρχείο, κάποια τυπογραφικά rules, και πιθανότητα ένα stylesheet (το οποίο συνηθίζω να αποκαλώ _base.scss), στο οποίο ορίζω διάφορα standard στύλ για HTML elements που χρησιμοποιούνται συνήθως.

  • _base.scss
  • _reset.scss
  • _typography.scss

Αν το project σας χρησιμοποιεί πολλά CSS animations, ίσως πρέπει να σκεφτείτε την προσθήκη του αρχείου \_animations.scss μέσα το οποίο θα περιέχει τους @keyframes ορισμούς απο όλα τα animations σας. Εάν τα χρησιμοποιείτε περιστασιακά, αφήστε τα να βρίσκονται μαζί τους selectors που τα χρησιμοποιούν.

Φάκελος Layout

Ο φάκελος layout/ περιέχει όλα όσα έχουν να κάνουν με την δομή του site ή την εφαρμογής. Αυτός ο φάκελος μπορεί να περιέχει stylesheets για τα βασικά κομμάτια του site (header, footer, navigation, sidebar…), το grid system ή ακόμα και CSS styles για όλα τα forms.

  • _grid.scss
  • _header.scss
  • _footer.scss
  • _sidebar.scss
  • _forms.scss
  • _navigation.scss

Ο layout/ φάκελος θα μπορούσε επίσης να ονομαστεί partials/, αναλόγως τι προτιμάτε.

Φάκελος Components

Για μικρότερα components, υπάρχει ο φάκελος components/. Ενώ ο φάκελος layout/ είναι macro (καθορίζει το γενικό μοντέλο της σελίδας), ο φάκελος components/ είναι επικεντρωμένος στα widgets. Περιέχει modules ολών των ειδών όπως slider, loader, widget, και οτιδήποτε άλλο προς αυτή την κατεύθυνση. Συνήθως υπάρχουν πολλά αρχεία μέσα στον φάκελο components/ αφού όλο το site ή η εφαρμογή απαρτίζεται απο μικρά modules.

  • _media.scss
  • _carousel.scss
  • _thumbnails.scss

Ο φάκελος components/ θα μπορούσε επίσης να ονομαστεί modules/, αναλόγος τι προτιμάται.

Φάκελος Pages

Αν έχετε στυλιστικό κώδικα μόνο για συγκεκριμένες σελίδες, είναι προτιμότερο να μπούν στον φάκελο pages/, σε ένα αρχείο που παίρνει το ονομά του από την σελίδα. Για παράδειγμα, δεν είναι ασυνήθιστο το να έχεις στυλιστικό κώδικα για την αρχική σελίδα εξού και η ανάγκη για ένα _home.scss αρχείο μέσα στον φάκελο pages/.

  • _home.scss
  • _contact.scss

Ανάλογα με τη διαδικασία deployment σας, αυτά τα αρχεία θα μπορούσαν να χρησιμοποιηθούν απο μόνα τους για να να αποφευχθεί η συγχώνευσή τους στο τελικό stylesheet. Είναι πραγματικά στο χέρι σας.

Φάκελος Themes

Σε μεγάλα sites και εφαρμογές, δεν είναι ασυνήθιστο το να έχεις διαφορετικά θέματα. Υπάρχουν πάρα πολλοί τρόποι για να αντιμετώπισης το θεμα αυτό αλλά προσωπικά προτιμό να τα έχω όλα μέσα στον φάκελο themes/.

  • _theme.scss
  • _admin.scss

Αυτό είναι πολύ συγκεκριμένο στο project και είναι πολύ σπάνιο να υπάρχει σε άλλα projects αφού τα περισσότερα μεγάλα sites και εφαρμογές έχουν διαφορετικές απαιτήσεις και αρχιτεκτονική.

Φάκελος Abstracts

Ο Φάκελος abstracts/ συγκεντρώνει όλα τα Sass tools και helpers που χρησιμοποιούνται σε όλο το project. Κάθε global μεταβλητή, συνάρτηση, mixin και placeholder πρέπει να μπει εκεί.

O γενικός κανόνας του φακέλου αυτού είναι ότι δεν πρέπει να παράγεται ούτε μια γραμμή CSS όταν γίνει compile μόνος του γιατί απλά αποτελείται από Sass helpers.

  • _variables.scss
  • _mixins.scss
  • _functions.scss
  • _placeholders.scss

Όταν δουλεύεις σε ένα πολύ μεγάλο project με πολλά αφηρημένα utilities, θα μπορούσες να τα ομαδοποιήσεις ανάλογα με το θέμα τους αντί για τον τύπο τους, για παράδειγμα τυπογραφία (_typography.scss), theming (_theming.scss), κτλ. Κάθε αρχείο περιέχει όλα τα σχετικά helpers: μεταβλητές, functions, mixins και placeholders. Με αυτόν τον τρόπο ο κώδικας μπορεί να γίνει πιο εύκολος στην ανάγνωση και την συντήρηση, ειδικά όταν τα αρχεία γίνονται πολύ μεγάλα.

Ο φάκελος abstracts/ θα μπορούσε επίσης να ονομαστεί utilities/ ή helpers/, αναλόγως τι προτιμάς.

Φάκελος Vendors

Και τελευταίο αλλά εξίσου σημαντικό, τα περισσότερα projects θα έχουν έναν vendors/ φάκελο ο οποίος περιέχει όλα τα CSS αρχεία από εξωτερικές βιβλιοθήκες όπως Normalize, Bootstrap, jQueryUI, FancyCarouselSliderjQueryPowered, και ούτω καθεξής. Βάζοντας τα σε αυτόν τον φάκελο είναι ένας καλός τρόπος για να πείς “Αυτός δεν είναι δικός μου κώδικας, δεν είναι δικιά μου ευθύνη”.

  • _normalize.scss
  • _bootstrap.scss
  • _jquery-ui.scss
  • _select2.scss

Αν θέλετε να παρακάμψετε τμήμα κάποιου vendor, προτείνω να φτιάξετε έναν όγδοο φάκελο με όνομα vendors-extensions/ στο οποίο μπορείς να βάλεις αρχεία που έχουν το ίδιο όνομα με το αρχείο που θες να παρακάμψεις απο τον φάκελο vendors.

Για παράδειγμα, vendors-extensions/_boostrap.scss είναι ένα αρχείο που περιέχει όλα τα CSS rules που αποσκοπούν στην εκ νέου δηλώση κάποιων απο τις προεπιλεγμένες του Bootstrap. Έτσι δεν χρειάζεται να να πειράξεις απευθείας τα vendor αρχεία, το οποίο γενικά δεν είναι καλή ιδέα.

Το αρχείο Main

Το αρχείο main (το οποίο συνήθως ονομάζεται main.scss) πρέπει να είναι το μόνο Sass αρχείο απο το ολόκληρο το code base του οποίου το όνομα δεν ξεκινάει απο τον χαρακτήρα της κάτω παύλας. Το αρχείο αυτό δεν πρέπει να περιέχει τίποτα εκτός απο γραμμές @import και σχολίων.

Τα αρχεία πρέπει να γίνονται import με βάση τον φάκελο στον οποίο βρίσκονται, το ένα μετά το άλλο με την ακόλουθη σειρά:

  1. abstracts/
  2. vendors/
  3. base/
  4. layout/
  5. components/
  6. pages/
  7. themes/

Προκειμένου να διατηρηθεί η αναγνωσιμότητα, το αρχείο main θα πρέπει να ακολουθεί τις παρακάτω κατευθυντήριες γραμμές:

  • ένα αρχείο ανα @import·
  • ενα @import ανα γραμμή·
  • καμία κενή γραμμή μεταξύ δύο import απο τον ίδιο φάκελο·
  • μια κενή γραμμή μετά απο το τελευταίο import ενός φακέλου·
  • οι επεκτάσεις των αρχείων και οι χαρακτήρες κάτω παύλας που προηγούνται πρέπει να παραλειφθούν.
@import 'abstracts/variables';
@import 'abstracts/functions';
@import 'abstracts/mixins';
@import 'abstracts/placeholders';

@import 'vendors/bootstrap';
@import 'vendors/jquery-ui';

@import 'base/reset';
@import 'base/typography';

@import 'layout/navigation';
@import 'layout/grid';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/sidebar';
@import 'layout/forms';

@import 'components/buttons';
@import 'components/carousel';
@import 'components/cover';
@import 'components/dropdown';

@import 'pages/home';
@import 'pages/contact';

@import 'themes/theme';
@import 'themes/admin';
@import vendors/bootstrap
@import vendors/jquery-ui

@import utils/variables
@import utils/functions
@import utils/mixins
@import utils/placeholders

@import base/reset
@import base/typography

@import layout/navigation
@import layout/grid
@import layout/header
@import layout/footer
@import layout/sidebar
@import layout/forms

@import components/buttons
@import components/carousel
@import components/cover
@import components/dropdown

@import pages/home
@import pages/contact

@import themes/theme
@import themes/admin

Υπάρχει ακόμα ένας τρόπος για να κάνεις import partial αρχεία τον οποίο θεωρώ έγκυρο. Στη θετική πλευρά, κάνει τα αρχεία ευανάγνωστα. Απο την άλλη, τα κάνει δυσκολότερα στην ενημέρωση. Εν πάση περιπτώσει, θα αφήσω εσάς να αποφασίσετε ποια είναι η καλύτερη, δεν έχει πολύ σημασία. Για αυτόν τον τρόπο, το αρχείο main θα πρέπει να τηρεί τις ακόλουθες κατευθυντήριες γραμμές:

  • ένα @import ανά φάκελο·
  • ένα linebreak ανά @import·
  • κάθε αρχεία σε δικιά του γραμμή·
  • μια κενή γραμμή μετά το τελευταίο import ενός φακέλου·
  • οι επεκτάσεις των αρχείων και οι χαρακτήρες κάτω παύλας που προηγούνται πρέπει να παραλειφθούν.
@import
  'abstracts/variables',
  'abstracts/functions',
  'abstracts/mixins',
  'abstracts/placeholders';

@import
  'vendors/bootstrap',
  'vendors/jquery-ui';

@import
  'base/reset',
  'base/typography';

@import
  'layout/navigation',
  'layout/grid',
  'layout/header',
  'layout/footer',
  'layout/sidebar',
  'layout/forms';

@import
  'components/buttons',
  'components/carousel',
  'components/cover',
  'components/dropdown';

@import
  'pages/home',
  'pages/contact';

@import
  'themes/theme',
  'themes/admin';
@import
  vendors/bootstrap,
  vendors/jquery-ui

@import
  utils/variables,
  utils/functions,
  utils/mixins,
  utils/placeholders

@import
  base/reset,
  base/typography

@import
  layout/navigation,
  layout/grid,
  layout/header,
  layout/footer,
  layout/sidebar,
  layout/forms

@import
  components/buttons,
  components/carousel,
  components/cover,
  components/dropdown

@import
  pages/home,
  pages/contact

@import
  themes/theme,
  themes/admin

Σχετικά με το globbing

Στον προγραμματισμό ηλεκτρονικών υπολογιστών, τα glob patterns καθορίζουν σύνολα ονομάτων αρχείων με χαρακτήρες μπαλαντέρ, όπως *.scss. Γενικά, το globbing είναι η επιλογή ενός σύνολο αρχείων βασιζόμενη σε ένα expression αντί κάποιας λίστας ονομάτων αρχείων. Όταν εφαρμόζεται στη Sass, σημαίνει το import partial αρχείων μέσα στο αρχείο main με ένα glob pattern αντί του να τα απαριθμήσουμε μεμονωμένα. Αυτό θα έκανε το αρχείο main να μοιάζει κάπως έτσι:

@import 'abstracts/*';
@import 'vendors/*';
@import 'base/*';
@import 'layout/*';
@import 'components/*';
@import 'pages/*';
@import 'themes/*';
@import 'abstracts/*'
@import 'vendors/*'
@import 'base/*'
@import 'layout/*'
@import 'components/*'
@import 'pages/*'
@import 'themes/*'

Η Sass δεν υποστηρίζει το file globbing απο μόνο του γιατί μπορεί να είναι μια επικίνδυνη λειτουργία γιατί στη CSS υπάρχει προτεραιότητα στην σειρά των δηλώσεων. Όταν κάνουμε import αρχείων δυναμικά (το οποίο συνήθως γίνεται με αλφαβητική σειρά), χάνεται ο έλεγχος της σειράς προτεραιότητας, κάτι που μπορεί να οδηγήσει σε παρενέργειες.

Κατόπιν αυτού, σε μια αυστηρή component-based αρχιτεκτονική με επιπλέον προσοχή προσοχή στο να μη διαρρεύσει κανένα style από ένα partial στο άλλο, η σειρά δεν θα ‘πρεπε να έχει σημασία πια, κάτι το οποίο θα επέτρεπε τα glob imports. Αυτό θα καθιστούσε ευκολότερη την πρόσθεση ή αφαίρεση partials, μιας και δεν θα ήταν πλέον απαραίτητη η προσεκτική ενημέρωση του αρχείου main.

Για τη χρήση Ruby Sass, υπάρχει ένα Ruby gem που ονομάζεται sass-globbing που επιτρέπει ακριβώς αυτή τη συμπεριφορά. Αν όμως χρησιμοποιείτε node-sass, μπορείτε να βασιστείτε είτε στο Node.js, είτε σε οποιοδήποτε build tool χρησιμοποιείτε για να κάνετε το compilation (Gulp, Grunt, etc.).

Το αρχείο Shame

Υπάρχει μια ενδιαφέρουσα ιδέα που έχει γίνει δημοφιλής απο τους Harry Roberts, Dave Rupert και Chris Coyier η οποία συνιστά να βάζουμε όλα τα CSS declarations, hacks και πράγματα για τα οποία δεν είμαστε περήφανοι μέσα στο αρχείο shame. Αυτό το αρχείο, με τον δραματικό τίτλο _shame.scss, θα γινόταν import μετά από όλα τα άλλα αρχεία στο τέλος του stylesheet.

/**
 * Διόρθωση του προβλήματος στο specificity του μενού πλοήγησης.
 *
 * Κάποιος χρησιμοποίησε ένα ID στον κώδικα του header (`#header a {}`) το οποίο υπερβαίνει
 * τα nav selectors (`.site-nav a {}`). Χρησιμοποιούμε !important για να το παρακάμψουμε μέχρι
 * να βρω χρόνο να διορθώσω τα θέματα του header.
 */
.site-nav a {
    color: #BADA55 !important;
}
/**
 * Διόρθωση του προβλήματος στο specificity του μενού πλοήγησης.
 *
 * Κάποιος χρησιμοποίησε ένα ID στον κώδικα του header (`#header a {}`) το οποίο υπερβαίνει
 * τα nav selectors (`.site-nav a {}`). Χρησιμοποιούμε !important για να το παρακάμψουμε μέχρι
 * να βρω χρόνο να διορθώσω τα θέματα του header.
 */
.site-nav a
    color: #BADA55 !important

Responsive Web Design και breakpoints

Δε νομίζω ότι χρειάζεται να κάνω κάποια εισαγωγή για το Responsive Web Design τώρα που βρίσκεται παντού. Πάραυτα μπορεί να αναρωτηθείς γιατί υπάρχει ενότητα για το RWD στο Sass styleguide; Βασικά υπάρχουν μερικά πράγματα που μπορούν να γίνουν για να δουλεύουμε πιο εύκολα με τα breakpoints, οπότε σκέφτηκα ότι δε θα ήταν κακή ιδέα να τα παρουσιάσω εδώ.

Ονομάζοντας τα breakpoints

Νομίζω μπορούμε να πούμε με ασφάλεια ότι τα media queries δεν πρέπει να δεσμεύονται σε συγκεκριμένες συσκευές. Για παράδειγμα, είναι πραγματικά κακή ιδέα να στοχεύουμε iPads ή κινητά Blackberry συγκεκριμένα. Τα media queries πρέπει να μεριμνούν για ένα εύρος μεγέθων οθονών, έως ότου ο σχεδιασμός να διακοπεί και το επόμενο media αναλαμβάνει.

Για τους ίδιους λόγους, τα breakpoints δεν πρέπει να ονομάζονται βάσει συσκευών αλλά με πιο γενικά κριτήρια. Ειδικά δεδομένου ότι μερικά κινητά είναι πλέον μεγαλύτερα από tablets, μερικά tablets μεγαλύτερα από μικρές οθόνες υπολογιστών, και πάει λέγοντας…

// Yep
$breakpoints: (
  'medium': (min-width: 800px),
  'large': (min-width: 1000px),
  'huge': (min-width: 1200px),
);

// Nope
$breakpoints: (
  'tablet': (min-width: 800px),
  'computer': (min-width: 1000px),
  'tv': (min-width: 1200px),
);
// Yep
$breakpoints: ('medium': (min-width: 800px), 'large': (min-width: 1000px), 'huge': (min-width: 1200px))

// Nope
$breakpoints: ('tablet': (min-width: 800px), 'computer': (min-width: 1000px), 'tv': (min-width: 1200px))

Σ’ αυτό το σημείο, οποιαδήποτε ονοματολογική σύμβαση ξεκαθαρίζει ότι ένα design δεν είναι άμεσα συνδεδεμένο με ένα συγκεκριμένο είδος συσκευής, μας κάνει, αρκεί να προκύπτει μια τάξη μεγέθους από το όνομα.

$breakpoints: (
  'σπόρος': (min-width: 800px),
  'βλαστός': (min-width: 1000px),
  'φυτό': (min-width: 1200px),
);
$breakpoints: ('σπόρος': (min-width: 800px), 'βλαστός': (min-width: 1000px), 'φυτό': (min-width: 1200px))

Τα προηγούμενα παραδείγματα χρησιμοποιούν nested maps για να καθορίσουν breakpoints, παρ’ όλ’ αυτά εξαρτάται από το είδος διαχειριστή των breakpoints που χρησιμοποιείς. Μπορείς να προτιμήσεις strings αντί για inner maps για μεγαλύτερη ευελιξία (π.χ. '(min-width: 800px)').

Διαχειριστής των breakpoints

Αφού έχεις ονομάσει τα breakpoints σου όπως θες, πρέπει να τα χρησιμοποιήσεις σε πραγματικά media queries. Υπάρχουν πολλοί τρόποι να το κάνεις αλλά είμαι μεγάλος υποστηρικτής του breakpoint map το οποίο διαβάζεται από μία getter συνάρτηση. Αυτό το σύστημα είναι απλό και αποτελεσματικό.

/// Responsive manager
/// @access public
/// @param {String} $breakpoint - Breakpoint
/// @requires $breakpoints
@mixin respond-to($breakpoint) {
  $raw-query: map-get($breakpoints, $breakpoint);

  @if $raw-query {
    $query: if(
      type-of($raw-query) == 'string',
      unquote($raw-query),
      inspect($raw-query)
    );

    @media #{$query} {
      @content;
    }
  } @else {
    @error 'Δεν βρέθηκε τιμή για το `#{$breakpoint}`. '
         + 'Παρακαλώ βεβαιώσου πως το έχεις ορίσει μέσα στο `$breakpoints` map.';
  }
}
/// Responsive manager
/// @access public
/// @param {String} $breakpoint - Breakpoint
/// @requires $breakpoints
=respond-to($breakpoint)
  $raw-query: map-get($breakpoints, $breakpoint)

  @if $raw-query
    $query: if(type-of($raw-query) == 'string', unquote($raw-query), inspect($raw-query))

    @media #{$query}
      @content

  @else
    @error 'Δεν βρέθηκε τιμή για το `#{$breakpoint}`. '
         + 'Παρακαλώ βεβαιώσου πως το έχεις ορίσει μέσα στο `$breakpoints` map.'

Προφανώς, πρόκειται για έναν απλουστευμένο διαχειριστή των breakpoints. Αν χρειάζεστε κάτι πιο ευέλικτο, προτείνω να μην ανακαλύψετε ξανά τον τροχό και να χρησιμοποιήσετε κάτι που έχει αποδειχτεί αποτελεσματικό όπως το Sass-MQ, το Breakpoint ή το include-media.

Αν ψάχνετε περισσότερο υλικό για διάβασμα στο πώς να προσεγγίσετε τα Media Queries στη Sass, τόσο το SitePoint (από τον υποφαινόμενο) όσο και το CSS-Tricks έχουν ωραία άρθρα σχετικά με το θέμα αυτό.

Χρήση των media Queries

Μέχρι πρόσφατα υπήρχε μια διαμάχη για το πού πρέπει να γράφονται τα media queries: ανήκουν μέσα στους selectors (η Sass το επιτρέπει) ή πρέπει να είναι αυστηρά εκτός; Μπορώ να πω ότι είμαι θερμός υποστηρικτής του συστήματος media-queries-μέσα-σε-selectors, γιατί πιστεύω πως συνδιάζεται καλά με τη λογική των components.

.foo {
  color: red;

  @include respond-to('medium') {
    color: blue;
  }
}
.foo
  color: red

  +respond-to('medium')
    color: blue

Παράγει το εξής αποτέλεσμα σε CSS:

.foo {
  color: red;
}

@media (min-width: 800px) {
  .foo {
    color: blue;
  }
}

Μπορεί να έχετε ακούσει ότι αυτή η σύμβαση οδηγεί σε επαναλαμβανόμενα media queries στη CSS. Πράγμα που ισχύει. Παρ’ όλα αυτά, έχουν γίνει δοκιμές και το πόρισμα είναι ότι δεν έχει σημασία εφόσον το Gzip (ή κάτι ανάλογο) έχει κάνει ό,τι κάνει:

… we hashed out whether there were performance implications of combining vs scattering Media Queries and came to the conclusion that the difference, while ugly, is minimal at worst, essentially non-existent at best.
— Sam Richards, regarding Breakpoint

Οπότε, αν πραγματικά σας απασχολούν τα επαναλαμβανόμενα media queries, μπορείτε να χρησιμοποιήσετε ένα εργαλείο για να τα συγχωνεύσετε όπως αυτό το gem, αν και πρέπει να προειδοποιήσω ότι μπορεί να έχετε παρενέργειες με το να μετακινείτε CSS πάνω-κάτω. Γνωρίζετε πολύ καλά ότι η σειρά του πηγαίου κώδικα είναι σημαντική.

Μεταβλητές

Οι μεταβλητές είναι η ουσία κάθε προγραμματιστικής γλώσσας. Μας επιτρέπουν να επαναχρησιμοποιούμε τιμές χωρίς να χρειάζεται να τις αντιγράφουμε ξανά και ξανά. Κυρίως, κάνουν την ενημέρωση μιας τιμής πολύ εύκολη. Δε χρειάζεται πλέον να κάνουμε αναζήτηση και αντικατάσταση ή χειροκίνητη σάρωση.

Παρ’ όλα αυτά η CCS δεν είναι τίποτα άλλο παρά ένα μεγάλο καλάθι που περιέχει όλα μας τα αβγά. Σε αντίθεση με πολλές γλώσσες, δεν υπάρχουν πραγματικά scopes στη CSS. Εξαιτίας αυτού, πρέπει να προσέχουμε πάρα πολύ όταν προσθέτουμε μεταβλητές διότι υπάρχει ο κίνδυνος να αντιμετωπίσουμε προβλήματα.

Η συμβουλή μου θα ήταν να δημιουργείτε μεταβλητές μόνο όταν έχει νόημα να το κάνετε. Μην αρχικοποιείτε μεταβλητές απλά για να το κάνετε, δεν θα βοηθήσει. Μία νέα μεταβλητή πρέπει να δημιουργείται μόνο όταν ισχύουν όλα τα παρακάτω κριτήρια:

  • η τιμή επαναλαμβάνεται τουλάχιστον δύο φορές·
  • η τιμή κατά πάσα πιθανότητα θα ανανεωθεί τουλάχιστον μια φορά·
  • όλες οι εμφανίσεις της τιμής είναι συνδεδεμένες με τη μεταβλητή (π.χ. όχι από σύμπτωση)

Βασικά, δεν υπάρχει λόγος να δηλώσουμε μια μεταβλητή η οποία δεν θα ανανεωθεί ποτέ ή χρησιμοποιείται σε ένα μόνο μέρος.

Scoping

Το scoping των μεταβλητών στη Sass έχει αλλάξει με τα χρόνια. Μέχρι αρκετά πρόσφατα, οι δηλώσεις μεταβλητών μέσα σε ένα σετ κανόνων και σε άλλα scopes ήταν τοπικές από προεπιλογή. Παρ’ όλα αυτά όταν υπήρχε ήδη μία global μεταβλητή με το ίδιο όνομα, η τοπική ανάθεση άλλαζε την global μεταβλητή. Από την έκδοση 3.4 και μετά, η Sass χειρίζεται σωστά την έννοια των scopes και δημιουργεί μια καινούρια τοπική μεταβλητή.

Τα έγγραφα αναφέρονται στην επισκίαση global μεταβλητής. Όταν δηλώνουμε μια μεταβλητή η οποία υπάρχει ήδη στο global scope, σε ένα εσωτερικό scope (selector, συνάρτηση, mixin…), λέμε ότι η τοπική μεταβλητή επισκιάζει την global. Βασικά, την παρακάμπτει μόνο για το local scope.

Το ακόλουθο κομμάτι κώδικα εξηγεί την έννοια της επισκίασης μεταβλητής.

// Αρχικοποίησε μια global μεταβλητή σε επίπεδο root.
$variable: 'initial value';

// Δημιούργησε ένα mixin που παρακάμπτει την global μεταβλητή.
@mixin global-variable-overriding {
  $variable: 'mixin value' !global;
}

.local-scope::before {
  // Δημιούργησε μια τοπική μεταβλητή που επισκιάζει την global.
  $variable: 'local value';

  // Κάνε include το mixin: παρακάμπτει την global μεταβλητή.
  @include global-variable-overriding;

  // Τύπωσε την τιμή της μεταβλητής.
  // Είναι η **τοπική** μεταβλητή, εφόσον επισκιάζει την global.
  content: $variable;
}

// Τύπωσε την μεταβλητή σε άλλον selector που δεν κάνει επισκίαση.
// Είναι η **global** μεταβλητή, όπως ήταν αναμενόμενο.
.other-local-scope::before {
  content: $variable;
}
// Αρχικοποίησε μια global μεταβλητή σε επίπεδο root.
$variable: 'initial value'

// Δημιούργησε ένα mixin που παρακάμπτει την global μεταβλητή.
@mixin global-variable-overriding
  $variable: 'mixin value' !global

.local-scope::before
  // Δημιούργησε μια τοπική μεταβλητή που επισκιάζει την global.
  $variable: 'local value'

  // Κάνε include το mixin: παρακάμπτει την global μεταβλητή.
  +global-variable-overriding

  // Τύπωσε την τιμή της μεταβλητής.
  // Είναι η **τοπική** μεταβλητή, εφόσον επισκιάζει την global.
  content: $variable

// Τύπωσε την μεταβλητή σε άλλον selector που δεν κάνει επισκίαση.
// Είναι η **global** μεταβλητή, όπως ήταν αναμενόμενο.
.other-local-scope::before
  content: $variable

!default flag

Όταν φτιάχνουμε μια βιβλιοθήκη, ένα framework, ένα grid system ή ένα άλλο κομμάτι Sass το οποίο έχουμε σκοπό να το διανείμουμε και να χρησιμοποιηθεί από εξωτερικούς developers, όλες οι μεταβλητές παραμετροποίησης πρέπει να δηλωθούν με το !default flag έτσι ώστε να μπορούν να αντικατασταθούν.

$baseline: 1em !default;
$baseline: 1em !default

Χάρη σ’ αυτό, ο developer μπορεί να ορίσει τη δική του μεταβλητή $baseline προτού εισάγει την βιλιοθήκη σας χωρίς να επαναπροσδιοριστεί η τιμή του.

// Η μεταβλητή του developer
$baseline: 2em;

// Η δήλωση `$baseline` της βιβλιοθήκης σας
@import 'your-library';

// $baseline == 2em;
// Η μεταβλητή του developer
$baseline: 2em

// Η δήλωση `$baseline` της βιβλιοθήκης σας
@import your-library

// $baseline == 2em

!global flag

Το !global flag πρέπει μόνο να χρησιμοποιείται όταν παρακάπτουμε μία global μεταβλητή από ένα τοπικό scope. Όταν δηλώνουμε μια μεταβλητή σε επίπεδο root, το !global flag πρέπει να παραλείπεται.

// Yep
$baseline: 2em;

// Nope
$baseline: 2em !global;
// Yep
$baseline: 2em

// Nope
$baseline: 2em !global

Πολλαπλές μεταβλητές ή maps

Υπάρχουν πλεονεκτήματα στο να χρησιμοποιούμε maps παρά να χρησιμοποιούμε πολλαπλές ξεχωριστές μεταβλητές. Το κυριότερο πλεονέκτημα είναι η δυνατότητα σάρωσης ενός map, πράγμα που δεν είναι δυνατό με ξεχωριστές μεταβλητές.

Ένα ακόμη πλεονέκτημα χρήσης maps είναι η δυνατότητα δημιουργίας μιας μικρής getter συνάρτησης για να παρέχουμε ένα πιο φιλικό API. Για παράδειγμα, δείτε τον παρακάτω κώδικα Sass:

/// Z-indexes map, το οποίο συλλέγει όλα τα επίπεδα Z της εφαρμογής
/// @access private
/// @type Map
/// @prop {String} key - όνομα επιπέδου
/// @prop {Number} value - τιμή Z για το key
$z-indexes: (
  'modal': 5000,
  'dropdown': 4000,
  'default': 1,
  'below': -1,
);

/// Πάρε την τιμή του z-index απο το όνομα του επίπεδου
/// @access public
/// @param {String} $layer - Το όνομα του επιπέδου
/// @return {Number}
/// @require $z-indexes
@function z($layer) {
  @return map-get($z-indexes, $layer);
}
/// Z-indexes map, το οποίο συλλέγει όλα τα επίπεδα Z της εφαρμογής
/// @access private
/// @type Map
/// @prop {String} key - όνομα επιπέδου
/// @prop {Number} value - τιμή Z για το key
$z-indexes: ('modal': 5000, 'dropdown': 4000, 'default': 1, 'below': -1,)

/// Πάρε την τιμή του z-index απο το όνομα του επίπεδου
/// @access public
/// @param {String} $layer - Το όνομα του επιπέδου
/// @return {Number}
/// @require $z-indexes
@function z($layer)
  @return map-get($z-indexes, $layer)

Extend

Το @extend directive είναι μία ισχυρή δυνατότητα που είναι συχνά παρεξηγημένη. Σε γενικές γραμμές, προσφέρει τη δυνατότητα να πεις στη Sass να σχεδιάσει ένα element A σαν να έχει επιλέξει έναν selector B. Περιττό να πούμε ότι αυτό μπορεί να γίνει πολύτιμος σύμμαχος όταν γράφουμε modular CSS.

Παρ’ όλ’ αυτά, ο πραγματικός σκοπός του @extend είναι να διατηρεί τις σχέσεις (περιορισμούς) εντός επεκταμένων selectors μεταξύ των rulesets. Τι σημαίνει αυτό ακριβώς;

  • Οι selectors έχουν περιορισμούς (π.χ. ο .bar στο .foo > .bar πρέπει να έχει έναν γονέα .foo);
  • Αυτοί οι περιορισμοί μεταφέρονται στον επεκταμένο selector (π.χ. ο .baz { @extend .bar; } θα παράξει .foo > .bar, .foo > .baz);
  • Τα declarations του επεκταμένου selector θα μοιραστούν με τον εκτεινόμενο selector.

Με βάση τα παραπάνω, είναι εμφανές πως η επέκταση των selectors με επιεικείς περιορισμούς μπορεί να οδηγήσει σε μια έκρηξη από selectors. Αν ο .baz .qux επεκτείνει τον .foo .bar, ο παραγόμενος selector μπορεί να είναι .foo .baz .qux ή .baz .foo .qux, αφού και ο .foo και ο .baz είναι πρόγονοι. Μπορεί να είναι γονείς, παπούδες, κ.ο.κ.

Πάντα να προσπαθείς να ορίζεις σχέσεις μέσω selector placeholders, όχι μέσω πραγματικών selectors. Αυτό θα σου δώσει την ελευθερία να χρησιμοποιήσεις (και να αλλάξεις) οποιοδήποτε naming convention έχεις ορίσει για τους selectors σου, και αφού οι σχέσεις ορίζονται μόνο μία φορά μέσα στους placeholders, υπάρχουν πολύ μικρότερες πιθανότητες να παράξεις απροσδόκητους selectors.

Για να κληρονομήσεις styles, χρησιμοποίησε το @extend μόνο αν ο εκτεινόμενος .class ή %placeholder selector είναι ένα είδος επεκταμένου selector. Για παράδειγμα, ένα .error είναι ένα είδος .warning, οπότε το .error μπορεί να κάνει @extend .warning.

%button {
  display: inline-block;
  // …  styles των κουμπιών

  // Σχέση: ένα %button που είναι παιδί ενός %modal
  %modal > & {
    display: block;
  }
}

.button {
  @extend %button;
}

// Yep
.modal {
  @extend %modal;
}

// Nope
.modal {
  @extend %modal;

  > .button {
    @extend %button;
  }
}
%button
  display: inline-block
  // … styles των κουμπιών

  // Σχέση: ένα %button που είναι παιδί ενός %modal
  %modal > &
    display: block

.button
  @extend %button

// Yep
.modal
  @extend %modal

// Nope
.modal
  @extend %modal

  > .button
    @extend %button

Υπάρχουν πολλά σενάρια στα οποία η επέκταση βοηθάει και αξίζει τον κόπο. Να θυμάσαι πάντα τους εξής κανόνες για να μπορείς να κάνεις @extend με προσοχή:

  • Να επεκτείνεις σε %placeholders κυρίως, όχι σε κανονικούς selectors.
  • Όταν επεκτείνεις κλάσεις, να επεκτείνεις μόνο μία κλάση από μία άλλη κλάση, ποτέ από ένα σύνθετο selector.
  • Να επεκτείνεις ευθέως έναν %placeholder όσο το δυνατόν λιγότερο.
  • Απόφυγε να επεκτείνεις γενικούς ancestor selectors (π.χ. .foo .bar) ή γενικούς sibling selectors (π.χ. .foo ~ .bar). Αυτό είναι που προκαλεί την έκρηξη από selectors.

Συχνά λένε ότι το @extend βοηθάει στο μέγεθος των αρχείων επειδή συνδιάζει selectors αντί να επαναλαμβάνει τα properties. Αυτό ισχύει, αν και η διαφορά είναι αμελητέα εφόσον γίνει συμπίεση με Gzip.

Κατόπιν αυτού, αν δε μπορείς να χρησιμοποιήσεις Gzip (ή κάτι παρόμοιο) τότε η προσέγγιση με το @extend μπορεί να φανεί πολύτιμη, ειδικά αν το βάρος του stylesheet επιβαρύνει το performance.

Επέκταση και media queries

Να επεκτείνεις selectors μόνο μέσα στο ίδιο media scope (@media directive). Σκέψου το media query ως άλλο ένα εμπόδιο.

%foo {
  content: 'foo';
}

// Nope
@media print {
  .bar {
    // Αυτό δεν δουλεύει. Ακόμα χειρότερα: διακόπτεται.
    @extend %foo;
  }
}

// Yep
@media print {
  .bar {
    @at-root (without: media) {
      @extend %foo;
    }
  }
}

// Yep
%foo {
  content: 'foo';

  &-print {
    @media print {
      content: 'foo print';
    }
  }
}

@media print {
  .bar {
    @extend %foo-print;
  }
}
%foo
  content: 'foo'

// Nope
@media print
  .bar
    // Αυτό δεν δουλεύει. Ακόμα χειρότερα: διακόπτεται.
    @extend %foo

// Yep
@media print
  .bar
    @at-root (without: media)
      @extend %foo

// Yep
%foo
  content: 'foo'

  &-print
    @media print
      content: 'foo print'

@media print
  .bar
    @extend %foo-print

Οι απόψεις φαίνεται να είναι άκρως αντιφατικές σχετικά με το @extend σε σημείο που πολλοί developers συμπεριλαμβανομένου και του εαυτού μου δεν υποστηρίζουν την χρήση του, όπως μπορείτε να διαβάσετε στα παρακάτω άρθρα:

Κατόπιν αυτού για να συνοψίσουμε, προτείνω την χρήση του @extend μόνο για την διατήρηση των σχέσεων εντός κάποιων selectors. Αν δύο selectors έχουν παρόμοια χαρακτηριστικά, τότε είναι η ιδανική περίπτωση για το @extend. Αν δεν έχουν κάποια σχέση αλλά μοιράζονται μερικά rules, ένα @mixin θα ταίριαζε καλύτερα. Περισσότερα για το πώς να επιλέξετε μεταξύ των δύο σε αυτό το άρθρο.

Ευχαριστώ τον David Khourshid για τη βοήθεια και την πείρα σ’ αυτή την ενότητα.

Mixins

Τα mixins είναι μία από τις πιο χρησιμοποιημένες δυνατότητες όλης της γλώσσας Sass. Είναι το κλειδί για το reusability και για DRY components. Και αυτό για έναν πολύ καλό λόγο: τα mixins επιτρέπουν τους δημιουργούς να ορίζουν styles τα οποία μπορούν να χρησιμοποιούν ξανά σε όλο το stylesheet χωρίς να χρειάζεται να καταφύγουν σε non-semantic κλάσεις όπως η .float-left.

Μπορούν να περιέχουν ολόκληρα CSS rules και λίγο πολύ οτιδήποτε είναι επιτρεπτό μέσα σε ένα έγγραφο Sass. Μπορούν ακόμα να δεχτούν arguments, ακριβώς όπως οι συναρτήσεις. Περιττό να πούμε ότι οι δυνατότητες είναι άπειρες.

Αλλά οφείλω να σε προειδοποιήσω εναντίον της κατάχρησης της ισχύος των mixins. Και πάλι, η λέξη κλειδή εδώ είναι απλότητα. Μπορεί να φαίνεται δελεαστικό να φτιάξεις παντοδύναμα mixins με τεράστια ποσά λογικής. Αυτό λέγεται over-engineering και οι περισσότεροι developers πάσχουν απ’ αυτό. Μην υπεραναλύεις τον κώδικά σου, και πάνω απ’ όλα κράτησέ τον απλό. Αν ένα mixin καταλήγει να είναι μακρύτερο από 20 γραμμές πάνω κάτω, τότε πρέπει να διαχωριστεί σε μικρότερα κομμάτια ή αναθεωρήσεις εντελώς.

Τα βασικά

Κατόπιν αυτού, τα mixins είναι πολύ χρήσιμα και καλό είναι να τα χρησιμοποιείς. Εμπειρικά, αν εντοπίσεις κάποια CSS properties τα οποία εμφανίζονται πάντα μαζί για κάποιο λόγο (δηλ. όχι τυχαία), μπορείς να τα βάλεις σε ένα mixin. Το micro-clearfix hack του Nicolas Gallagher αξίζει να μπει σε ένα (argumentless) mixin για παράδειγμα.

/// Βοηθητικό mixin για να καθαρίσει τα εσωτερικά floats
/// @author Nicolas Gallagher
/// @link http://nicolasgallagher.com/micro-clearfix-hack/ Micro Clearfix
@mixin clearfix {
  &::after {
    content: '';
    display: table;
    clear: both;
  }
}
/// Βοηθητικό mixin για να καθαρίσει τα εσωτερικά floats
/// @author Nicolas Gallagher
/// @link http://nicolasgallagher.com/micro-clearfix-hack/ Micro Clearfix
@mixin clearfix
  &::after
    content: ''
    display: table
    clear: both

Άλλο ένα αξιόλογο παράδειγμα θα ήταν ένα mixin που θα ορίζει το μέγεθος ενός element, ορίζονται το width και το height ταυτόχρονα. Όχι μόνο θα έκανε τον κώδικα ελαφρύτερο κατά την πληκτρολόγηση, αλλά και πιο ευανάγνωστο.

/// Βοηθητικό mixin για να ορίσουμε τις διαστάσεις ενός element
/// @author Kitty Giraudel
/// @param {Length} $width
/// @param {Length} $height
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}
/// Βοηθητικό mixin για να ορίσουμε τις διαστάσεις ενός element
/// @author Kitty Giraudel
/// @param {Length} $width
/// @param {Length} $height
=size($width, $height: $width)
  width: $width
  height: $height

Για πιο περίπλοκα παραδείγματα των mixins, ρίξτε μια ματιά στα this mixin to generate CSS triangles, this mixin to create long shadows και this mixin to polyfill CSS gradients for old browsers.

Mixins χωρίς arguments

Μερικές φορές τα mixins χρησιμοποιούνται για να αποφύγουμε να επαναλαμβάνουμε τα ίδια declarations ξανά και ξανά, χωρίς να χρειάζονται παράμετρους ή έχοντας παράμετρους με εύλογες προεπιλεγμένες τιμές (sensible defaults) ώστε να μην χρειάζεται να περάσουμε arguments.

Σ’ αυτές τις περιπτώσεις, μπορούμε να παραλείξουμε άφοβα τις παρενθέσεις όταν τα καλούμε. Το @include keyword (ή το σύμβολο + σε indented-syntax) ήδη λειτουργεί ως δείκτης ότι σ’ αυτή η γραμμή καλείται ένα mixin· δε χρειάζονται επιπλέον παρενθέσεις εδώ.

// Yep
.foo {
  @include center;
}

// Nope
.foo {
  @include center();
}
// Yep
.foo
  +center

// Nope
.foo
  +center()

Arguments list

Όταν έχουμε να κάνουμε με έναν άγνωστο αριθμό από arguments σε ένα mixin, χρησιμοποιούμε πάντα μία arglist αντί για μία λίστα. Δες την arglist ως το όγδοο κρύφο undocumented data type της Sass που χρησιμοποιείται έμμεσα όταν περνάμε έναν αυθαίρετο αριθμό από arguments σε ένα mixin ή σε μία συνάρτηση της οποίας το signature περιέχει ....

@mixin shadows($shadows...) {
  // type-of($shadows) == 'arglist'
  // …
}
=shadows($shadows...)
  // type-of($shadows) == 'arglist'
  // …

Οπότε, όταν φτιάχνουμε ένα mixin που δέχεται αρκετά arguments (ας πούμε 3 ή περισσότερα), σκέψου καλά πριν τα συγχωνεύσεις σε μία λίστα ή ένα map με τη λογική ότι θα είναι πιο εύκολο από το να τα περάσεις ένα-ένα.

Η Sass είναι αρκετά έξυπνη όσον αφορά τα declarations στα mixins και στις συναρτήσεις, τόσο που μπορείς να περάσεις μία λίστα ή ένα map ως arglist σε μία συνάρτηση function/mixin ώστε να την αναγνωρίσει ως μια σειρά από arguments.

@mixin dummy($a, $b, $c) {
  // …
}

// Yep
@include dummy(true, 42, 'kittens');

// Yep but nope
$params: (true, 42, 'kittens');
$value: dummy(nth($params, 1), nth($params, 2), nth($params, 3));

// Yep
$params: (true, 42, 'kittens');
@include dummy($params...);

// Yep
$params: (
  'c': 'kittens',
  'a': true,
  'b': 42,
);
@include dummy($params...);
=dummy($a, $b, $c)
  // …

// Yep
+dummy(true, 42, 'kittens')

// Yep but nope
$params: (true, 42, 'kittens')
$value: dummy(nth($params, 1), nth($params, 2), nth($params, 3))

// Yep
$params: (true, 42, 'kittens')
+dummy($params...)

// Yep
$params: ('c': 'kittens', 'a': true, 'b': 42,)
+dummy($params...)

Σχετικά με το αν είναι βέλτιστο να χρησιμοποιείτε πολλαπλά arguments, μια list ή ένα argument list, το SitePoint έχει ένα καλό άρθρο πάνω στο θέμα.

Mixins και vendor prefixes

Μπορεί να είναι δελεαστικό να ορίσεις δικά σου mixins που διαχειρίζονται τα vendor prefixes για ιδιότητες της CSS που υποστηρίζονται μερικώς ή καθόλου. Αλλά βασικά δε θέλουμε να το κάνουμε αυτό. Πρώτον, αν μπορείς να χρησιμοποιήσεις το Autoprefixer, χρησιμοποίησε το Autoprefixer. Θα αφαιρέσει τον κώδικα της Sass από το project σου, θα είναι πάντα ενημερωμένο και οπωσδήποτε θα κάνει καλύτερη δουλειά απο σένα στο να κάνεις prefix διάφορα.

Δυστυχώς, το Autoprefixer δε μπορεί να χρησιμοποιηθεί παντού. Αν χρησιμοποιείς Bourbon ή Compass, μάλλον θα ξέρεις πως και τα δύο προσφέρουν μία συλλογή από mixins που διαχειρίζονται τα vendor prefixes για σένα. Χρησιμοποίησέ τα.

Αν δε μπορείς να χρησιμοποιήσεις το Autoprefixer ούτε το Bourbon ούτε το Compass, τότε και μόνο τότε μπορείς να φτιάξεις το δικό σου mixin για να κάνεις prefix τις ιδιότητες της CSS. Αλλά. Μην φτιάξεις ένα mixin για κάθε ιδιότητα που εκτυπώνε όλους τους vendors.

// Nope
@mixin transform($value) {
  -webkit-transform: $value;
  -moz-transform: $value;
  transform: $value;
}
// Nope
=transform($value)
  -webkit-transform: $value
  -moz-transform: $value
  transform: $value

Κάντο με τον έξυπνο τρόπο.

/// Βοηθητικό mixin για να την παραγωγή vendor prefixes
/// @access public
/// @author KittyGiraudel
/// @param {String} $property - Unprefixed CSS property
/// @param {*} $value - Ακατέργαστη τιμή CSS
/// @param {List} $prefixes - Λίστα απο prefixes για παραγωγή
@mixin prefix($property, $value, $prefixes: ()) {
  @each $prefix in $prefixes {
    -#{$prefix}-#{$property}: $value;
  }

  #{$property}: $value;
}
/// Βοηθητικό mixin για να την παραγωγή vendor prefixes
/// @access public
/// @author KittyGiraudel
/// @param {String} $property - Unprefixed CSS property
/// @param {*} $value - Ακατέργαστη τιμή CSS
/// @param {List} $prefixes - Λίστα απο prefixes για παραγωγή
=prefix($property, $value, $prefixes: ())
  @each $prefix in $prefixes
    -#{$prefix}-#{$property}: $value

  #{$property}: $value

Οπότε μετά η χρήση αυτού του mixin είναι προφανής:

.foo {
  @include prefix(transform, rotate(90deg), ('webkit', 'ms'));
}
.foo
  +prefix(transform, rotate(90deg), ('webkit', 'ms'))

Μην ξεχνάς ότι αυτή είναι μία κακή λύση. Για παράδειγμα, δε μπορεί να διαχειριστεί σύνθετα polyfills όπως αυτά που χρειάζονται για το Flexbox. Μ’ αυτή τη λογική, η χρήση του Autoprefixer είναι πολύ καλύτερη λύση.

Conditional statements

Πιθανώς ήδη γνωρίζετε ότι η Sass παρέχει conditional statements μέσω των @if και @else directives. Εκτός αν έχετε κάποια περίπλoκη λογική στον κώδικα σας, δεν συντρέχει λόγος χρήσης των conditional statements στα stylesheets σας. Στην πραγματικότητα, υπάρχουν κυρίως για την χρήση τους σε libraries και frameworks.

Εν πάση περιπτώσει, αν ποτέ βρεθείτε στην ανάγκη τους, παρακαλώ να σεβαστείτε τα ακόλουθα guidelines:

  • αποφεύγετε τις παρενθέσεις εκτός και αν είναι αναγκαίο·
  • Να αφήνεις πάντα καινούργια γραμμή πριν απο ένα @if·
  • Να αφήνεις πάντα καινούργια γραμμή μετά το αριστερό άγκιστρο ({
  • Το @else πρέπει να είναι στην ίδια γραμμή με το αριστερό άγκιστρο ({) του προηγούμενου @if·
  • Να αφήνεις πάντα καινούργια γραμμή μετά το τελευταίο δεξί άγκιστρο (}).
// Yep
@if $support-legacy {
  // …
} @else {
  // …
}

// Nope
@if ($support-legacy == true) {
  // …
}
@else {
  // …
}
// Yep
@if $support-legacy
  // …
@else
  // …

// Nope
@if ($support-legacy == true)
  // …
@else
  // …

Όταν ελέγχετε για κάποια μη-αληθή τιμή, να χρησιμοποιείτε πάντα την λέξη κλειδή not αντί να ελέγχετε για false ή null.

// Yep
@if not index($list, $item) {
  // …
}

// Nope
@if index($list, $item) == null {
  // …
}
// Yep
@if not index($list, $item)
  // …

// Nope
@if index($list, $item) == null
  // …

Πάντα να βάζετε την μεταβλητή στην αριστερή πλευρά της δήλωσης και το (μη) αναμενόμενο αποτέλεσμα στην δεξιά πλευρά. Ανεστραμμένα conditional statements είναι συνήθως δυσκολότερα στην ανάγνωση, ειδικά απο τους άπειρους developers.

// Yep
@if $value == 42 {
  // …
}

// Nope
@if 42 == $value {
  // …
}
// Yep
@if $value == 42
  // …

// Nope
@if 42 == $value
  // …

Όταν χρησημοποιείτε conditional statements μέσα σε κάποιο function το οποίο θα επιστρέφει διαφορετική τιμή ανάλογα με τις προϋποθέσεις του conditional statement, βεβαιωθείτε πάντα ότι το function εξακολουθεί να έχει @return έξω από το πλαίσιο του conditional statement.

// Yep
@function dummy($condition) {
  @if $condition {
    @return true;
  }

  @return false;
}

// Nope
@function dummy($condition) {
  @if $condition {
    @return true;
  } @else {
    @return false;
  }
}
// Yep
@function dummy($condition)
  @if $condition
    @return true

  @return false;

// Nope
@function dummy($condition)
  @if $condition
    @return true
  @else
    @return false

Loops

Επειδή η Sass παρέχει πολύπλοκες δομές δεδομένων όπως lists και maps, δεν αποτελεί έκπληξη το γεγονός ότι δίνει επίσης τη δυνατότητα στους συγγραφείς να μπορούν να σαρώσουν τις εν λόγω οντότητες.

Ωστόσο, η παρουσία των loops συνήθως υποδηλώνει σχετικά σύνθετη λογική που κατά πάσα πιθανότητα δεν ανήκει στη Sass. Πριν χρησιμοποιήσεις ένα loop, βεβαιώσου ότι έχει νόημα και ότι λύνει πραγματικά ένα πρόβλημα.

Each

Από τα τρία loops που παρέχονται από τη Sass, το @each loop είναι σίγουρα αυτό που χρησιμοποιείται πιο συχνά. Παρέχει ένα καθαρό API για να μπορείς να σαρώσεις ένα list ή ένα map.

@each $theme in $themes {
  .section-#{$theme} {
    background-color: map-get($colors, $theme);
  }
}
@each $theme in $themes
  .section-#{$theme}
    background-color: map-get($colors, $theme)

Οταν σαρώνεις ένα map, πρέπει πάντοτε να χρησιμοποιείς $key και $value ως ονόματα στις μεταβλητές για να υπάρχει συνοχή.

@each $key, $value in $map {
  .section-#{$key} {
    background-color: $value;
  }
}
@each $key, $value in $map
  .section-#{$key}
    background-color: $value

Επίσης, φρόντισε να σεβαστείς τα παρακάτω guidelines για τη διατήρηση της αναγνωσιμότητας:

  • Να αφήνεις πάντα καινούργια γραμμή πριν το @each·
  • Να αφήνεις πάντα καινούργια γραμμή μετά το δεξί άγκιστρο (}) εκτός και εαν η επόμενη γραμμή περιέχει δεξί άγκιστρο (}).

For

Το @for loop μπορεί να φανεί χρήσιμο όταν συνδυάζεται με τις ψευδοκλάσεις της CSS’ :nth-*. Εκτός από αυτά τα σενάρια, να προτιμάς ένα @each loop αν πρέπει να σαρώσεις κάτι.

@for $i from 1 through 10 {
  .foo:nth-of-type(#{$i}) {
    border-color: hsl($i * 36, 50%, 50%);
  }
}
@for $i from 1 through 10
  .foo:nth-of-type(#{$i})
    border-color: hsl($i * 36, 50%, 50%)

Να χρησιμοποιείς πάντα $i ως όνομα μεταβλητής για να διατηρήσεις τη συνήθη σύμβαση, και μην χρησιμοποιείς ποτέ την λέξη κλειδί to: πάντοτε να χρησιμοποιείς through, εκτός κι αν έχεις πραγματικά καλό λόγο. Πολλοί developers δεν γνωρίζουν καν ότι η Sass προσφέρει αυτή την παραλλαγή· με τη χρήση του ενδέχεται να προκαλέσει σύγχυση.

Επίσης, φρόντισε να σεβαστείς τα παρακάτω guidelines για τη διατήρηση της αναγνωσιμότητας:

  • Να αφήνεις πάντα καινούργια γραμμή πριν το @for·
  • Να αφήνεις πάντα καινούργια γραμμή μετα το δεξί άγκιστρο (}) εκτός και εαν η επόμενη γραμμή περιέχει το δεξί άγκιστρο (}).

While

Το @while loop δεν έχει καμία απολύτως χρησιμότητα σε ένα πραγματικό Sass project, ιδίως επειδή δεν υπάρχει τρόπος να κάνεις break από το loop από μέσα. Μην το χρησιμοποιήσεις.

Προειδοποιήσεις και σφάλματα

Αν υπάρχει μια λειτουργία που παραβλέπεται συχνά από αυτούς που γράφουν Sass, αυτή είναι η δυνατότητα να εμφανίζονται δυναμικά προειδοποιήσεις και σφάλματα. Πράγματι, η Sass διαθέτει τρία ειδικά directives για να εμφανίζει περιεχόμενο στο standard output system (CLI, compiling app…):

  • @debug·
  • @warn·
  • @error.

Ας βγάλουμε στην άκρη το @debug μιας και προφανώς προορίζεται για το debugging της SassScript, πράγμα που δε μας αφορά εδώ. Οπότε μένουμε με το @warn και το @error τα οποία είναι εμφανώς ίδια, εκτός του ότι το ένα διακόπτει τον compiler ενώ το άλλο όχι. Σας αφήνω να μαντέψετε ποιο κάνει τί.

Τώρα, υπάρχουν πολλά περιθώρια σε ένα Sass project για προειδοποιήσεις και σφάλματα. Βασικά οποιοδήποτε mixin ή function προσδοκεί ένα συγκεκριμένο τύπο ή argument θα μπορούσε να πετάξει ένα σφάλμα αν κάτι πάει στραβά, ή να εμφανίσει μία προειδοποίηση όταν υποθέτει κάτι.

Προειδοποιήσεις

Δείτε αυτό το function του Sass-MQ που επιδιώκει να μετατρέψει μια τιμή από px σε em, για παράδειγμα:

@function mq-px2em($px, $base-font-size: $mq-base-font-size) {
  @if unitless($px) {
    @warn 'Assuming #{$px} to be in pixels, attempting to convert it into pixels.';
    @return mq-px2em($px + 0px);
  } @else if unit($px) == em {
    @return $px;
  }
   @return ($px / $base-font-size) * 1em;
}
@function mq-px2em($px, $base-font-size: $mq-base-font-size)
  @if unitless($px)
    @warn 'Assuming #{$px} to be in pixels, attempting to convert it into pixels.'
    @return mq-px2em($px + 0px)
  @else if unit($px) == em
    @return $px
   @return ($px / $base-font-size) * 1em

Εάν η τιμή δεν έχει μονάδα μέτρησης, το function θεωρεί πως η τιμή εκφράζεται σε pixels. Σ’ αυτό το σημείο, μια υπόθεση ίσως είναι ριψοκίνδυνη, οπότε ο χρήστης θα έπρεπε να προειδοποιηθεί ότι το πρόγραμμα έκανε κάτι που θα μπορούσε να θεωρηθεί απροσδόκητο.

Σφάλματα

Τα σφάλματα, σε αντίθεση με τις προειδοποιήσεις, αποτρέπουν τον compiler να συνεχίσει παρακάτω. Βασικά διακόπτουν το compilation και εμφανίζουν ένα μήνυμα στο output stream όπως και στο stack trace, πράγμα που βοηθάει στο debugging. Εξαιτίας όλων αυτών, τα σφάλματα πρέπει να πετιούνται όταν δεν υπάρχει τρόπος να συνεχιστεί η εκτέλεση του προγράμματος. Όπου είναι εφικτό, δοκίμασε να διορθώσεις το πρόβλημα και προτίμησε να εμφανίσεις μία προειδοποίηση.

Για παράδειγμα, ας πούμε ότι φτιάχνεις μία βελτιωμένη συνάρτηση για να έχεις πρόσβαση σε τιμές από ένα συγκεκριμένο map. Θα μπορούσε να πετάξεις ένα σφάλμα αν το ζητούμενο key δεν υπάρχει στο map.

/// Z-indexes map, το οποίο συλλέγει όλα τα επίπεδα Z της εφαρμογής
/// @access private
/// @type Map
/// @prop {String} key - όνομα επιπέδου
/// @prop {Number} value - τιμή Z για το key
$z-indexes: (
  'modal': 5000,
  'dropdown': 4000,
  'default': 1,
  'below': -1,
);

/// Πάρε την τιμή του z-index απο το όνομα του επίπεδου
/// @access public
/// @param {String} $layer - όνομα του επιπέδου
/// @return {Number}
/// @require $z-indexes
@function z($layer) {
  @if not map-has-key($z-indexes, $layer) {
    @error 'Δεν υπάρχει επίπεδο με όνομα `#{$layer}` μέσα στα $z-indexes. '
         + 'Το επίπεδο πρέπει να είναι ένα απο τα #{map-keys($z-indexes)}.';
  }

  @return map-get($z-indexes, $layer);
}
/// Z-indexes map, το οποίο συλλέγει όλα τα επίπεδα Z της εφαρμογής
/// @access private
/// @type Map
/// @prop {String} key - όνομα επιπέδου
/// @prop {Number} value - τιμή Z για το key
$z-indexes: ('modal': 5000, 'dropdown': 4000, 'default': 1, 'below': -1,)

/// Πάρε την τιμή του z-index απο το όνομα του επίπεδου
/// @access public
/// @param {String} $layer - όνομα του επιπέδου
/// @return {Number}
/// @require $z-indexes
@function z($layer)
  @if not map-has-key($z-indexes, $layer)
    @error 'Δεν υπάρχει επίπεδο με όνομα `#{$layer}` μέσα στα $z-indexes. '
         + 'Το επίπεδο πρέπει να είναι ένα απο τα #{map-keys($z-indexes)}.'

  @return map-get($z-indexes, $layer)

Για περισσότερες πληροφορίες σχετικά με το πώς να χρησιμοποιήσετε το @error αποτελεσματικά, αυτή η εισαγωγή στο error handling θα βοηθούσε.

Εργαλεία

Το καλό με έναν CSS preprocessor τόσο δημοφιλή όσο η Sass είναι ότι έχει ένα ολόκληρο οικοσύστημα από frameworks, plugins, βιβλιοθήκες και εργαλεία. Μετά από 8 χρόνια ύπαρξης, κοντεύουμε όλο και περισσότερο στο σημείο που οτιδήποτε μπορεί να γραφτεί σε Sass, έχει γραφτεί σε Sass.

Παρόλα αυτά η συμβουλή μου είναι να ελαχιστοποιήσετε τον αριθμό των εξαρτήσεων στα απολύτως απαραίτητα. Το να διαχειρίζεστε εξαρτήσεις είναι ένας μπελάς στον οποίο δεν θέλετε να μπλέξετε. Επίσης, η ανάγκη για εξωτερικές εξαρτήσεις στη Sass είναι μικρή έως ανύπαρκτη.

Compass

Το Compass είναι το κυριότερο Sass framework που κυκλοφορεί. Έχοντας αναπτυχθεί από τον Chris Eppstein, έναν από τους δύο βασικούς σχεδιαστές της Sass, δεν το βλέπω να χάνει δραματικά σε δημοτικότητα για το επόμενο διάστημα, αν θέλετε τη γνώμη μου.

Όμως, δεν χρησιμοποιώ το Compass πια, κυρίως επειδή καθυστερεί πολύ την Sass. Η Ruby Sass είναι αρκετά αργή από μόνη της, οπότε το να προσθέτουμε περισσότερη Ruby και περισσότερη Sass δεν βοηθάει.

Το θέμα είναι ότι χρησιμοποιούμε πολύ λίγο από το όλο framework. Το Compass είναι τεράστιο. Τα mixins για συμβατότητα μεταξύ των browsers είναι απλά η κορυφή του παγόβουνου. Μαθηματικές συναρτήσεις, βοηθοί εικόνας, εργαλεία για sprites… Υπάρχουν ένα σωρό πράγματα που μπορούν να γίνουν με αυτό το θαυμάσιο λογισμικό.

Δυστυχώς, όλα αυτά είναι μικρής σημασίας και δεν υπάρχει κάποιο πραγματικά δυνατό χαρακτηριστικό εκεί μέσα. Μια εξαίρεση θα μπορούσε να γίνει για τον sprite builder που είναι πραγματικά υπέροχος, αλλά το Grunticon και το Grumpicon κάνουν την ίδια δουλειά και έχουν το πλεονέκτημα ότι μπορούν να ενσωματωθούν στην διαδικασία του build.

Τελοσπάντων, δεν απαγορεύω την χρήση του Compass αλλά και ούτε το προτείνω, κυρίως επειδή δεν είναι συμβατό με την LibSass (αν και έχουν γίνει προσπάθειες προς αυτή την κατεύθυνση). Αν αισθάνεστε καλύτερα χρησιμοποιώντας το, κάντε το, αλλά δεν νομίζω ότι θα κερδίσετε πολλά από αυτό τελικά.

Η Ruby Sass δέχεται αυτή τη στιγμή σημαντικές βελτιστοποιήσεις που στοχεύουν συγκριμένα στα styles που περιέχουν πολλή λογική με πολλές συναρτήσεις και mixins. Αυτές θα βελτιώσουν δραματικά την απόδοση σε σημείο που το Compass και άλλα frameworks δεν θα καθυστερούν πια τη Sass.

Συστήματα Grid

Το να μην χρησιμοποιείτε ένα σύστημα grid δεν είναι πλέον επιλογή τώρα που το Responsive Web Design είναι παντού. Για να κάνουμε τα designs να φαίνονται ομοιόμορφα και σταθερά σε όλα τα μεγέθη, χρησιμοποιούμε κάποιου είδους grid για να τοποθετούμε τα αντικείμενα. Για να αποφύγουμε να γράφουμε τον κώδικα του grid ξανά και ξανά, μερικά λαμπρά μυαλά έκαναν τα δικά τους grid επαναχρησιμοποιήσιμα.

Για να το θέσω σαφώς: Δεν πολυσυμπαθώ τα συστήματα grid. Φυσικά και βλέπω τις προοπτικές, αλλά νομίζω ότι τα περισσότερα από αυτά είναι τελείως υπερβολικά και χρησιμοποιούνται κυρίως για να σχεδιάζουμε κόκκινες στήλες σε λευκό φόντο στις σημειώσεις ομιλιών κάποιων φυτών designers. Πότε ήταν η τελευταία φορά που σκέφτηκατε πάλι καλά που έχω αυτό το εργαλείο για να στήσω αυτό το 2-5-3.1-π grid; Πολύ σωστά, ποτέ. Επειδή τις περισσότερες φορές, απλά θέλετε το συνηθισμένο κανονικό grid με 12 στήλες, τίποτα φανταχτερό.

Αν χρησιμοποιείτε ένα CSS framework για το project σας όπως το Bootstrap ή το Foundation, κατά πάσα πιθανότητα αυτό εμπεριέχει ήδη ένα σύστημα grid και σε αυτή την περίπτωση συνιστώ να το χρησιμοποιήσετε για να αποφύγετε να ασχοληθείτε με ακόμα μία εξάρτηση.

Αν δεν είστε προσκολλημένοι σε ένα συγκεκριμένο σύστημα grid, θα χαρείτε να μάθετε ότι υπάρχουν δύο κορυφαίες μηχανές grid γραμμένες σε Sass: το Susy και το Singularity. Και οι δύο κάνουν πολλά περισσότερα από ότι θα χρειαστείτε ποτέ οπότε μπορείτε να διαλέξετε αυτήν που προτιμάτε μεταξύ των δύο και να είστε σίγουροι ότι όλες οι ακραίες περιπτώσεις σας—ακόμη και οι πιο παράξενες—θα έχουν καλυφθεί. Αν με ρωτάτε, το Suzy έχει λίγο καλύτερη κοινότητα, αλλά αυτή είναι απλά η γνώμη μου.

Αλλιώς μπορείς να απευθυνθείτε σε κάτι πιο απλό, όπως το csswizardry-grids. Τελικά, η επιλογή δεν θα έχει σημαντικό αντίκτυπο στο δικό σας στυλ κώδικα, οπότε είναι λίγο πολύ στο χέρι σας ποιο θα διαλέξετε.

SCSS-lint

Ο έλεγχος (lint) του κώδικα είναι πολύ σημαντικός. Συνήθως, το να ακολουθείτε οδηγίες από ένα styleguide βοηθάει να ελαχιστοποιηθούν τα λάθη ποιότητας του κώδικα, αλλά κανείς δεν είναι τέλειος και πάντοτε υπάρχουν πράγματα που επιδέχονται βελτίωση. Έτσι μπορείτε να πείτε ότι ο έλεγχος του κώδικα είναι εξίσου σημαντικός όσο και ο σχολιασμός του.

Το SCSS-lint είναι ένα εργαλείο που σε βοηθάει να κρατάτε τα SCSS αρχεία σας καθαρά και ευανάγνωστα. Είναι πλήρως παραμετροποιήσιμο και εύκολο να ενσωματωθεί στα εργαλεία σας.

Ευτυχώς, οι προτεινόμενες ρυθμίσεις του SCSS-lint είναι αρκετά παρόμοιες με αυτές που περιγράφονται στο παρόν έγγραφο. Αν θέλετε να παραμετροποιήσετε το SCSS-lint σύμφωνα με τα Sass Guidelines, προτείνω το ακόλουθο setup:

linters:

  BangFormat:
    enabled: true
    space_before_bang: true
    space_after_bang: false

  BemDepth:
    enabled: true
    max_elements: 1

  BorderZero:
    enabled: true
    convention: zero

  ChainedClasses:
    enabled: false

  ColorKeyword:
    enabled: true

  ColorVariable:
    enabled: false

  Comment:
    enabled: false

  DebugStatement:
    enabled: true

  DeclarationOrder:
    enabled: true

  DisableLinterReason:
    enabled: true

  DuplicateProperty:
    enabled: false

  ElsePlacement:
    enabled: true
    style: same_line

  EmptyLineBetweenBlocks:
    enabled: true
    ignore_single_line_blocks: true

  EmptyRule:
    enabled: true

  ExtendDirective:
    enabled: false

  FinalNewline:
    enabled: true
    present: true

  HexLength:
    enabled: true
    style: short

  HexNotation:
    enabled: true
    style: lowercase

  HexValidation:
    enabled: true

  IdSelector:
    enabled: true

  ImportantRule:
    enabled: false

  ImportPath:
    enabled: true
    leading_underscore: false
    filename_extension: false

  Indentation:
    enabled: true
    allow_non_nested_indentation: true
    character: space
    width: 2

  LeadingZero:
    enabled: true
    style: include_zero

  MergeableSelector:
    enabled: false
    force_nesting: false

  NameFormat:
    enabled: true
    convention: hyphenated_lowercase
    allow_leading_underscore: true

  NestingDepth:
    enabled: true
    max_depth: 1

  PlaceholderInExtend:
    enabled: true

  PrivateNamingConvention:
    enabled: true
    prefix: _

  PropertyCount:
    enabled: false

  PropertySortOrder:
    enabled: false

  PropertySpelling:
    enabled: true
    extra_properties: []

  PropertyUnits:
    enabled: false

  PseudoElement:
    enabled: true

  QualifyingElement:
    enabled: true
    allow_element_with_attribute: false
    allow_element_with_class: false
    allow_element_with_id: false

  SelectorDepth:
    enabled: true
    max_depth: 3

  SelectorFormat:
    enabled: true
    convention: hyphenated_lowercase
    class_convention: '^(?:u|is|has)\-[a-z][a-zA-Z0-9]*$|^(?!u|is|has)[a-zA-Z][a-zA-Z0-9]*(?:\-[a-z][a-zA-Z0-9]*)?(?:\-\-[a-z][a-zA-Z0-9]*)?$'

  Shorthand:
    enabled: true

  SingleLinePerProperty:
    enabled: true
    allow_single_line_rule_sets: false

  SingleLinePerSelector:
    enabled: true

  SpaceAfterComma:
    enabled: true

  SpaceAfterPropertyColon:
    enabled: true
    style: one_space

  SpaceAfterPropertyName:
    enabled: true

  SpaceAfterVariableColon:
    enabled: true
    style: at_least_one_space

  SpaceAfterVariableName:
    enabled: true

  SpaceAroundOperator:
    enabled: true
    style: one_space

  SpaceBeforeBrace:
    enabled: true
    style: space
    allow_single_line_padding: true

  SpaceBetweenParens:
    enabled: true
    spaces: 0

  StringQuotes:
    enabled: true
    style: single_quotes

  TrailingSemicolon:
    enabled: true

  TrailingZero:
    enabled: true

  TransitionAll:
    enabled: false

  UnnecessaryMantissa:
    enabled: true

  UnnecessaryParentReference:
    enabled: true

  UrlFormat:
    enabled: false

  UrlQuotes:
    enabled: true

  VariableForProperty:
    enabled: false

  VendorPrefixes:
    enabled: true
    identifier_list: base
    include: []
    exclude: []

  ZeroUnit:
    enabled: true

Αν δεν είστε πεπεισμένοι για την αναγκαιότητα της χρήσης του SCSS-lint, σας προτείνω να διαβάσετε αυτά τα εξαιρετικά άρθρα: Clean Up your Sass with SCSS-lint, Improving Sass code quality on theguardian.com και An Auto-Enforceable SCSS Styleguide.

Αν θέλετε να ενσωματώσετε έλεγχο της SCSS στην διακασία Grunt build, θα χαρείτε να μάθετε ότι υπάρχει ένα Grunt plugin γι’ αυτό το σκοπό που λέγεται grunt-scss-lint.

Επίσης αν ψάχνετε μια ωραία εφαρμογή που να δουλεύει με το SCSS-lint και παρόμοια εργαλεία, οι τύποι στην Thoughtbot (Bourbon, Neat…) δουλεύουν πάνω στο Hound.

Ανακεφαλαίωση

Τα guidelines αυτά είναι αρκετά εκτενή και καμιά φορά είναι χρήσιμο να υπάρχουν συνοπτικά σε μια πιο σύντομη έκδοση. Η περίληψη παρακάτω.

Βασικές αρχές

  • Η ύπαρξη ενός styleguide έχει να κάνει με τη συνοχή. Αν διαφωνείς με κάποιους κανόνες των Sass Guidelines, δεν τρέχει κάτι, αρκεί να διατηρείς τη συνοχή.
  • Η Sass πρέπει να διατηρείται όσο πιο απλή γίνεται. Αποφεύγουμε πολύπλοκα συστήματα εκτός κι αν είναι απολύτως αναγκαία.
  • Να θυμάσαι ότι το KISS (Keep It Simple, Stupid) είναι καλύτερο από το DRY (Don’t Repeat Yourself).

Σύνταξη & μορφοποίηση

  • Μία εσοχή (indentation) αποτελείται από δύο (2) κενά, όχι tabs.
  • Οι αράδες πρέπει να είναι — όσο το δυνατόν — μικρότερες από 80 χαρακτήρες. Μη διστάσεις να τις διαχωρίσεις σε πολλές γραμμές όταν είναι απαραίτητο.
  • Η CSS πρέπει να είναι ορθά γραμμένη, ενδεχομένως ακολουθώντας τα CSS Guidelines του Harry Roberts.
  • Δεν υπάρχουν περιορισμοί στα κενά, χρησιμοποίησέ τα για να διαχωρίσεις αντικείμενα, κανόνες και ορίσματα. Μη διστάσεις να αφήσεις κενές γραμμές, ποτέ δε βλάπτει.

Strings

  • Συνιστάται ιδιαίτερα να ορίζουμε το @charset directive στην κορυφή του stylesheet.
  • Με εξαίρεση τους CSS identifiers, τα strings πρέπει να έχουν μονά εισαγωγικά (‘). Τα URL επίσης θέλουν εισαγωγικά.

Αριθμοί

  • Η Sass δεν κάνει διαχωρισμούς ανάμεσα σε αριθμούς, ακέραιους και δεκαδικούς, οπότε τα μηδενικά (0) μετά το τέλος ενός δεκαδικού πρέπει να παραλείπονται. Πάραυτα, τα μηδενικά πριν από τους δεκαδικούς μικρότερους της μονάδας (0) βοηθάνε στην αναγνωσιμότητα και πρέπει να εμφανίζονται.
  • Ένα μηδενικό (0) μήκος δεν πρέπει να έχει μονάδα μέτρησης.
  • Οι μετατροπές μονάδων μέτρησης πρέπει να υπολογίζονται με αριθμητικές πράξεις, όχι με string operations.
  • Για τη βελτίωση της αναγνωσιμότητας, οι υπολογισμοί υψηλού επιπέδου πρέπει να μπαίνουν σε παρένθεση. Επίσης, σύνθετες μαθηματικές πράξεις πρέπει να διαχωρίζονται σε μικρότερα κομμάτια.
  • Οι μαγικοί αριθμοί βλάπτουν δραματικά την συντηρησιμότητα του κώδικα και πρέπει πάντα να αποφεύγονται. Όταν δεν είσαι σίγουρος, να εξηγείς εκτενώς τον αμφιλεγόμενο αριθμό.

Χρώματα

  • Τα χρώματα πρέπει να ορίζονται σε HSL προτίστως, μετά σε RGB, και μετά σε δεκαεξαδικούς (με πεζούς χαρακτήρες και συντομευμένη μορφή). Τα ονόματα των χρωμάτων καλό είναι να αποφεύγονται.
  • Προτιμούμε το mix(..) αντί για το darken(..) και το lighten(..) όταν λευκαίνουμε ή σκουραίνουμε ένα χρώμα.

Lists

  • Τα lists πρέπει να διαχωρίζονται με κόμμα, εκτός όταν χρησιμοποιούνται για απευθείας αντιστοίχιση σε CSS values που διαχωρίζονται με κενά.
  • Η χρήση παρενθέσεων επίσης βοηθάει στη βελτίωση της αναγνωσιμότητας.
  • Τα lists που ορίζονται σε μία γραμμή δεν πρέπει να έχουν κόμμα στο τέλος, ενώ τα lists σε πολλαπλές γραμμές πρέπει να έχουν.

Maps

  • Τα maps που περιέχουν περισσότερα από ένα ζεύγος γράφονται σε πολλαπλές γραμμές.
  • Για να βοηθήσουμε τη συντηρησιμότητα, το τελευταίο ζεύγος ενός map πρέπει να τελειώνει με κόμμα.
  • Τα keys των maps που τυγχάνει να είναι strings πρέπει να έχουν εισαγωγικά όπως όλα τα strings.

Ταξινόμηση των ορισμάτων (declarations)

  • Η λογική που χρησιμοποιούμε για να ταξινομήσουμε τα ορίσματα (αλφαβητικά, ανά τύπο, κλπ.) δεν έχει σημασία αρκεί να υπάρχει συνοχή.

Εμφώλευση των selectors (nesting)

  • Αποφεύγουμε να εμφωλεύουμε selectors όταν δε χρειάζεται (στις περισσότερες περιπτώσεις).
  • Χρησιμοποιούμε την εμφώλευση των selectors για ψευδο-κλάσεις και ψευδο-αντικείμενα.
  • Τα media queries μπορούν επίσης να εμφωλευτούν μέσα στον selector που τους αντιστοιχεί.

Συμβάσεις ονομασιών

  • Το καλύτερο είναι να ακολουθούμε τις συμβάσεις ονομασιών της CSS, οι οποίες (με εξαίρεση κάποια λάθη) είναι με πεζούς χαρακτήρες και διαχωρίζονται με παύλες.

Σχόλια

  • Η CSS είναι ζόρικη γλώσσα· μη διστάσεις να γράψεις εκτενέστατα σχόλια για οτιδήποτε φαίνεται (ή είναι) ασαφές.
  • Όσον αφορά μεταβλητές, συναρτήσεις, mixins και placeholders που καθορίζουν κάποιο public API, χρησιμοποιούμε SassDoc σχόλια.

Μεταβλητές

  • Μη χρησιμοποιείς το !default flag για οποιαδήποτε μεταβλητή είναι μέρος κάποιου public API και μπορεί να αλλαχτεί άφοβα.
  • Μη χρησιμοποιείς το !global flag στο root level γιατί μπορεί μελλοντικά να θεωρηθεί συντακτική παράβαση στη Sass.

Extend

  • Να επεκτείνεις μόνο placeholders, όχι πραγματικούς CSS selectors.
  • Να επεκτείνεις έναν placeholder όσο το δυνατόν λιγότερες φορές για να αποφύγεις παρενέργειες.
Επιστροφή στην κορυφή