Właściwa struktura zasobów SCSS w szynach

86

Mam więc app/assets/stylesheets/strukturę katalogów, która wygląda mniej więcej tak:

   |-dialogs
   |-mixins
   |---buttons
   |---gradients
   |---vendor_support
   |---widgets
   |-pages
   |-structure
   |-ui_elements

W każdym katalogu znajduje się wiele części sass (zwykle * .css.scss, ale jeden lub dwa * .css.scss.erb).

Mogę dużo przypuszczać, ale rails POWINNY automatycznie kompilować wszystkie pliki w tych katalogach z powodu *= require_tree .w application.css, prawda?

Niedawno próbowałem zmienić te pliki, usuwając wszystkie zmienne kolorów i umieszczając je w pliku w app/assets/stylesheetsfolderze głównym (_colors.css.scss). Następnie utworzyłem plik w app/assets/stylesheetsfolderze głównym o nazwie master.css.scss, który wygląda następująco:

// Color Palette 
@import "colors";

// Mixins
@import "mixins/buttons/standard_button";
@import "mixins/gradients/table_header_fade";
@import "mixins/vendor_support/rounded_corners";
@import "mixins/vendor_support/rounded_corners_top";
@import "mixins/vendor_support/box_shadow";
@import "mixins/vendor_support/opacity";

Nie bardzo rozumiem, jak rails radzą sobie z porządkiem kompilacji zasobów, ale oczywiście nie jest to na moją korzyść. Wygląda na to, że żaden z plików nie zdaje sobie sprawy, że ma importowane zmienne lub miksy, więc generuje błędy i nie mogę skompilować.

Undefined variable: "$dialog_divider_color".
  (in /home/blah/app/assets/stylesheets/dialogs/dialog.css.scss.erb)

Undefined mixin 'rounded_corners'.
  (in /home/blah/app/assets/stylesheets/widgets.css.scss)

Zmienna $dialog_divider_colorjest jasno zdefiniowana w _colors.css.scss i _master.css.scssimportuje kolory oraz wszystkie moje miksy. Ale najwyraźniej rails nie dostały tej notatki.

Czy istnieje sposób, w jaki mogę naprawić te błędy, czy też będę musiał uciec się do umieszczenia wszystkich moich definicji zmiennych z powrotem w każdym pojedynczym pliku, jak również wszystkich importowanych plików mixin?

Niestety, ten facet nie wydaje się sądzić, że to możliwe, ale mam nadzieję, że się myli. Wszelkie myśli są mile widziane.

David Savage
źródło

Odpowiedzi:

126

Problem z CSS polega na tym, że nie chcesz automatycznie dodawać wszystkich plików. Istotna jest kolejność wczytywania i przetwarzania arkuszy przez przeglądarkę. Dlatego zawsze będziesz jawnie importować cały plik CSS.

Na przykład załóżmy, że masz arkusz normalize.css , aby uzyskać domyślny wygląd zamiast wszystkich okropnych różnych implementacji przeglądarek. Powinien to być pierwszy plik wczytywany przez przeglądarkę. Jeśli umieścisz ten arkusz w dowolnym miejscu w importowanym pliku css, nie tylko nadpisze on domyślne style przeglądarki, ale także wszelkie style zdefiniowane we wszystkich plikach css, które zostały wcześniej załadowane. To samo dotyczy zmiennych i mixinów.

Po obejrzeniu prezentacji Roya Tomeija na Euruko2012 zdecydowałem się na następujące podejście, jeśli masz dużo CSS do zarządzania.

Generalnie używam tego podejścia:

  1. Zmień nazwę wszystkich istniejących plików .css na .scss
  2. Usuń całą zawartość z pliku application.scss

Zacznij dodawać dyrektywy @import do application.scss.

Jeśli używasz Twittera i kilku własnych arkuszy CSS, musisz najpierw zaimportować bootstrap, ponieważ zawiera on arkusz do resetowania stylów. Więc dodajesz @import "bootstrap/bootstrap.scss";do swojego application.scss.

Plik bootstrap.scss wygląda następująco:

// CSS Reset
@import "reset.scss";

// Core
@import "variables.scss";
@import "mixins.scss";

// Grid system and page structure
@import "scaffolding.scss";

// Styled patterns and elements
@import "type.scss";
@import "forms.scss";
@import "tables.scss";
@import "patterns.scss";

Twój application.scssplik wygląda następująco:

@import "bootstrap/bootstrap.scss";

Ze względu na kolejność importów można teraz używać zmiennych załadowanych @import "variables.scss";w dowolnym innym .scsspliku zaimportowanym po nim. Można ich więc używać w type.scssfolderze bootstrap, ale także w my_model.css.scss.

Następnie utwórz folder o nazwie partialslub modules. To będzie miejsce dla większości innych plików. Możesz po prostu dodać import do application.scsspliku, aby wyglądał następująco:

@import "bootstrap/bootstrap.scss";
@import "partials/*";

Teraz, jeśli zrobisz trochę css, aby stylizować artykuł na swojej stronie głównej. Po prostu utwórz partials/_article.scssi zostanie dodany do skompilowanego application.css. Ze względu na kolejność importu możesz również używać dowolnych mikserów bootstrap i zmiennych we własnych plikach scss.

Jedyną wadą tej metody, jaką do tej pory znalazłem, jest to, że czasami musisz wymusić rekompilację częściowych plików / *. Scss, ponieważ rails nie zawsze robią to za Ciebie.

Benjamin Udink ten Cate
źródło
4
Dzięki, to jest podejście, które ostatecznie wybrałem. Nienawidzę tego rozwiązania, ponieważ musisz ręcznie dodawać każdy plik, który dołączasz, za każdym razem, gdy go tworzysz, ale wydaje się, że w przypadku CSS porządek ma znaczenie. Normalnie powiedziałbym, że to po prostu złe pisanie CSS, ale jeśli używasz rzeczy dostarczonych przez producenta (np. Bootstrap, jak wspomniałeś powyżej), masz tendencję do nadpisywania rzeczy, więc niechętnie się zgadzam, że jest to właściwe podejście.
David Savage,
1
Dzięki! Bardzo fajne podejście i dobre wyjaśnienie! Jeden projekt, o którym warto wspomnieć: Ta wtyczka FireBug, FireSass , pokazuje numer linii z my_model.css.sass zamiast linii skompilowanej application.css
BBQ Chef
Przy takim podejściu, jeśli chcę zmienić domyślny kolor linku na czerwony, który dotyczy wszystkich moich podszablonów, jak byś to zrobił? Gdzie byś to położył?
chh
1
Zwróć uwagę, że takie podejście jest również zalecane w Przewodniku po szynach do zarządzania zasobami . Wyszukaj „Jeśli chcesz użyć wielu plików Sass”.
ybakos
1
@Lykos tak, usuniesz wszystko z application.cssi zmień nazwę na application.scss. Ponieważ require_treezawiera wszystko i zwykle chcesz kontrolować zamówienie
Benjamin Udink ten Cate
8

aby używać zmiennych itp. między plikami, musisz użyć dyrektywy @import. pliki są importowane w określonej kolejności.

następnie użyj application.css, aby zażądać pliku, który deklaruje import. to jest sposób na osiągnięcie pożądanej kontroli.

na koniec w swoim pliku layout.erb możesz określić, który "główny" plik css ma być używany

przykład będzie bardziej pomocny:

załóżmy, że masz w swojej aplikacji dwa moduły, które wymagają różnych zestawów CSS: „aplikacja” i „admin”

pliki

|-app/
|-- assets/
|--- stylesheets/
|     // the "master" files that will be called by the layout
|---- application.css
|---- application_admin.css
|
|     // the files that contain styles
|---- config.scss
|---- styles.scss
|---- admin_styles.scss
|
|     // the files that define the imports
|---- app_imports.scss
|---- admin_imports.scss
|
|
|-- views/
|--- layouts/
|---- admin.html.haml
|---- application.html.haml

tak wyglądają pliki w środku:

-------- THE STYLES

-- config.scss
// declare variables and mixins
$font-size: 20px;

--  app_imports.scss
// using imports lets you use variables from `config` in `styles`
@import 'config'
@import 'styles'

-- admin_imports.scss
// for admin module, we import an additional stylesheet
@import 'config'
@import 'styles'
@import 'admin_styles'

-- application.css
// in the master application file, we require the imports
*= require app_imports
*= require some_other_stylesheet_like_a_plugin
*= require_self

-- application_admin.css
// in the master admin file, we require the admin imports
*= require admin_imports
*= require some_other_stylesheet_like_a_plugin
*= require_self


-------- THE LAYOUTS

-- application.html.haml
// in the application layout, we call the master css file
= stylesheet_link_tag "application", media: "all"

--  admin.html.haml
// in the admin layout, we call the admin master css file
= stylesheet_link_tag "application_admin", media: "all"
Doug Lee
źródło
7

Utwórz następującą strukturę folderów:

+ assets
|
--+ base
| |
| --+ mixins (with subfolders as noted in your question)
|
--+ styles
  |
  --+ ...

W folderze baseutwórz plik „globals.css.scss”. W tym pliku zadeklaruj wszystkie swoje importy:

@import 'base/colors';
@import 'base/mixins/...';
@import 'base/mixins/...';

W twoim application.css.scss powinieneś mieć:

*= require_self
*= depends_on ./base/globals.css.scss
*= require_tree ./styles

I na koniec (to jest ważne), zadeklaruj @import 'base/globals'w każdym pliku stylu, w którym chcesz używać zmiennych lub miksów. Możesz wziąć to pod uwagę, ale w rzeczywistości podoba mi się pomysł, że musisz deklarować zależności swoich stylów w każdym pliku. Oczywiście ważne jest, aby importować tylko elementy mieszane i zmienne w pliku globals.css.scss, ponieważ nie dodają one definicji stylów. W przeciwnym razie definicje stylów zostałyby dołączone wiele razy w twoim prekompilowanym pliku ...

wstyd
źródło
Wypróbowałem to, ale nigdy nie mogłem sprawić, by działał poprawnie (nadal brakowało błędów zmiennych). Może nie zrobiłem czegoś poprawnie, ale myślę, że ostatecznie podejście polegające na ręcznym wyświetlaniu każdego pliku jest właściwą drogą, ponieważ w przypadku CSS kolejność ma znaczenie.
David Savage,
Nadal możesz zastosować następujące podejście: zamiast „require_tree ...” po prostu dodaj wiersz dla każdego pojedynczego pliku. Spowoduje to, że SASS zaimportuje pliki we właściwej kolejności. Jeśli chodzi o rozwiązanie, z którego teraz korzystasz, zapoznaj się z tym postem, który właśnie znalazłem: stackoverflow.com/questions/7046495/ ... Upewnij się, że w pliku application.css została umieszczona instrukcja depends_on.
zawstydzony
4

Zgodnie z tym pytaniem możesz używać TYLKOapplication.css.sass w celu zdefiniowania importu i udostępniania zmiennych między szablonami.

=> Wydaje się, że to tylko kwestia nazwy.

Innym sposobem może być uwzględnienie wszystkiego i wyłączenie tego potoku .

Coren
źródło
Jest to w zasadzie to samo, co Benjamin Udink ten Cate zaoferował w drugiej części twojej odpowiedzi, ale przyznanie mu nagrody jako jego rozwiązania jest bardziej wyjaśniające.
David Savage,
1

Miałem bardzo podobny problem. Pomogło mi wstawienie podkreślenia do instrukcji @import podczas importowania części. Więc

@import "_base";

zamiast

@import "base";

To może być dziwny błąd ...

Bernát
źródło