TeaCSS – очевидный подход к созданию стилей

    Введение


    Хотя CSS-подобных языков не так уж и много – на слуху и на плаву сейчас и вовсе одни Sass и Less, я все же начну с ответа на вопрос «А зачем нужен еще один?».

    Если коротко, то TeaCSS не плодит новых сущностей, ведь этот тот же CSS, в который в качестве языка добавили JavaScript.

    У этого подхода есть свои плюсы и минусы.

    Плюсы – практически никаких подводных камней. Файл tea преобразуется в JavaScript, наполненный простыми командами вывода. Этот JavaScript можно отлаживать, смотреть в FireBug и вообще его поведение предсказуемо. В этом и состоит очевидность подхода, в вебе уже и так превалирует JS, поэтому вам не придется учить ничего нового.


    Минусы – вы все-таки лишаетесь некоторых сущностей предметной области CSS, таких как «пиксели», «проценты» и т.п. Вы работаете с CSS именно как с текстом. Придется писать width:@{100+200}px, а не width: 100px + 200px.

    TeaCSS – это именно шаблонизатор или препроцессор CSS, а не новый язык, который нужно учить. В этом его сила и слабость.

    Я планировал написать несколько статей, в которых с нарастающей сложностью будет рассказываться про язык и те задачи, для которых он был разработан.

    В этой (первой) статье, я расскажу по то, как включить TeaCSS в ваш цикл разработки и покажу с примерами то, что не умеют делать Sass и Less, но умеет герой сегодняшней статьи.

    Короткий туториал


    Для тех, кто пользуется Sass и Less, можно читать по диагонали.

    Есть всего 3 простых вещи, который позволяют описать TeaCSS. Это:
    1. Внедренный яваскрипт
    2. Вложенные правила
    3. Миксины (необязательны и могут заменены на п.1 )

    Внедренный яваскрипт

    Во время процессинга CSS вы можете исполнить любой JS-код. Он будет исполнен ровно как написано. При помощи этого вы можете объявлять переменные, функции, делать циклы, чтоб объявить схожие правила, генерировать картинки, рисовать вспомогательные интерфейсы, да и вообще – делать что душе угодно.

    Исполнять код просто:
    /* однострочный код */
    @ var baseColor = 'red';
    

    Или так:
    /* много строчек */
    @{
        var baseColor = ‘red’;
        var borderColor = darken(‘red’,20);
    }
    


    Потом внутри любого CSS правила вы можете использовать JS-выражения.

    Например:
    body { color: @baseColor; } // для простых выражений, или
    /* со скобками @{} чтоб явно указать конец выражения */
    body { color: @{lighten(baseColor,Math.sin(3.14)); }
    

    Вложенные правила

    Позволяют писать составные селекторы лесенкой.
    /* TeaCSS */
    #header {
      h1 {
        font-size: 26px;
        font-weight: bold;
      }
      p { font-size: 12px;
        a { 
          text-decoration: none;
          &:hover { border-width: 1px }
        }
      }
    }
    

    Миксины

    Синтаксический сахар, похожи на LESS-овские. Позволяют сохранить набор правил для использования в другом контексте с параметрами.
    .my_mixin(color) {  // - это лишь сахар для my_mixin = function (color) {
      color: @color;
    }
    body {
      .my_mixin('red');
    }
    

    За более подробными объяснениями отсылаю на сайт – teacss.org

    Как подключить к своему проекту

    Очень важно понимать, что teacss не претендует на то, чтоб становится заменой CSS в продакшене, это именно решение для разработки, поэтому должен существовать как способ превращения TeaCSS в обычный CSS, так и способ посмотреть, что получается прямо в браузере.

    Для просмотра все просто.
    <link tea="style.tea">
    <script src="teacss.js"></script>
    

    И вуаля, вы увидите превью вашего стиля на текущей странице. В своих проектах я использую что-то вроде.
    <? if $applicationMode=='development' ?>
        <link tea="style.tea">
        <script src="teacss.js"></script>
    <? else ?>
        <link type="text/css" rel="stylesheet" href="style.css">
    <? endif ?>
    

    Остается вопрос, как превратить *.tea в *.css

    В ранних версиях для этого была консольная утилита, но т.к. TeaCSS стал поддерживать различные чисто-браузерные фишки, вроде Canvas, то было решено оставить этот вопрос на откуп разработчику. Вы можете вывести простой интерфейс для сохранения файла и решить, что делать с полученным текстом. В целом это часть большой концепции «Ваш код – это и есть ваша IDE», о которой я расскажу в дальнейших статьях, а пока просто добавьте в дев-версию верстки что-то вроде:
    <script>
    teacss.buildCallback(function(files){
        $.post(location.href,{css:files['default.css']});
    });
    <? if (isset($_POST['css']) file_put_contents('your/location/style.css', $_POST['css']) ?>
    </script>
    

    Аналогично, на других серверных языках (кстати, pull request-ы приветствуются).

    Создание картинок на лету


    Первый и единственный в этой статье фокус.

    По сути, встроенный в teacss Canvas – это обертка над канвасом браузера, такая обертка, которая позволяет использовать WebGL и обычный двумерный контекст для генерирования изображений.

    При переводе *.tea в *.css вы можете сохранить сгенерированную картинку в файл. Зачем это нужно?

    Рассказывая о переменных в CSS и приводя примеры DRY подхода к разработке разработчики того же LESS лукавят. Не всякий дизайн можно сверстать чистым CSS, сайты, где это достижимо скорее в меньшинстве. Большая часть хитрых бордюров, фонов, шапок и т.п. тоже зависят от этих переменных (базовых цветов, например), поэтому после простой замены значения, вам нужно открывать Фотошоп и перерисовывать или изменять графику для сайта.

    С teacss вы можете сделать графику параметрической тоже (пример из документации):
    @ color1 = 'red';
    @ color2 = 'blue';
    body {
    @{
        // pretty clear here, huh?
        var canvas = new Canvas("background.png");
        canvas.replaceColors( {
          '#ffae00':color1,
          '#f7e7ba':lighten(color1,30),
          '#705551':color2
        });
        // display
        canvas.background('bg.png');
      }
    }
    


    Для более полного понимания принципу покажу генерацию фона с 0. Например, полосатого фона. Кстати, можно оформить как миксин и потом использовать в последующих проектах.

    .background_striped(size,width,color) {
        @{
            (new Canvas(size,size))
            .draw2D(function(ctx){
                ctx.beginPath();
                ctx.lineWidth = width;
                ctx.strokeStyle = color.toString();
                ctx.moveTo(-size,-size); ctx.lineTo(size*2,size*2);
                ctx.moveTo(-size-size,-size); ctx.lineTo(size,size*2);
                ctx.moveTo(-size+size,-size); ctx.lineTo(size*3,size*2);
                ctx.stroke();
            })
            .background();
        }
    }
    

    И вот пример использования:
    teacss.org/stripes.htm

    Заключение


    Как, наверное, стало понятно из предыдущего раздела, teacss – это не совсем шаблонизатор CSS т.к. выдает на выход, например, еще и картинки. Скажу больше, его можно использовать как универсальный шаблонизатор и генерировать им мобильные интерфейсы, шаблоны и скрипты.

    Как раз про это и про архитектуру самого проекта я расскажу в следующей статье.

    P.S.


    Я решил написать эту статью т.к. по сути делаю вещи очень похожие одновременно на последний проект Adobe — Brackets и на кикстартеровский Light-table. Но есть несколько отличий и мне кажется, что они могут быть ключевыми, чтоб рассказать про них, нужно начать с библиотеки, которая лежит в основе проекта — teacss. Надеюсь найти на хабре единомышленников и помощников/партнеров этот самый большой проект.

    Комментарии 28

      0
      Как-то неудобно видеть смесь JavaScript и CSS в одном файле, но пример с полосками (идеей такого подхода) весьма интересен.
      По скорости не скажете, как быстро он обрабатывает 1,10,100… Кб *.tea файла (в общем, и, возможно, чуть конкретнее, какие конструкции более тормозят)?
        +1
        сам парсинг дешевый
        скорость выполнения JS в современных браузерах очень высокая
        тем более выполнять tea нужно только при разработке
        операции с канвасом достаточно быстрые т.к. используется WebGL,
        можете потыкать тут — evanw.github.com/glfx.js/demo/, очень похожий принцип

        в дальнейшем я опишу, как добавить гуи для дизайнеров прямо в dev версию странички и тогда можно будет буквально «поиграть с цветом», как любят писать забавные заказчики

        смесь JS и CSS нужна только при написании библиотек (вы или программист пишете сложные библиотеки генерации графики на том же JS, а потом просто вызываете нужный миксин)
        если вы используете только переменные и миксины, то синтаксис практически не отличается от LESS

        т.е.

        @ my_color = 'blue';
        body {
            color: @my_color;
        }
        
        +7
        Вы так и не ответили на самый первый вопрос — зачем? «не плодит новых сущностей» — так себе повод читать статью дальше и тем более изучать новый язык.
          0
          нет никакого нового языка, в этом и ответ
          при этом сам факт, что можно при генерации использовать JS, позволяет представить, что можно сделать
          если сразу само собой не представляется, то я расскажу в серии статей дальше ( про общую сборку проекта, тестирование, подвязку к общему билду, генерацию иструментов для дизайнера и IDE, как часть страницы )

          первый пример есть в этой статье — про изображения, при этом синтаксис такой же как у HTML Canvas
          все пихать в одну статью — накладно

          надеюсь, у вас хватит терпения подождать
          даже для этой статьи мне пришлось подчистить сайт, убрать с него остальные возможности т.к. они очень хорошо документированы

          сам процесс перевода библиотек из местного использования в паблик — это дополнительная работа ) и ее нужно сделать, чтоб вам, в том числе, легче жилось ( ну при условии, что вы ответите для себя на вопрос «зачем» )
            0
            * не очень хорошо документированы
              0
              вы говорите: «вот есть такая штука — вы сами можете представить что с ней можно сделать». это неправильный подход, если, конечно, вы хотите кого-то заинтересовать.
              надо как-то так: «работать вам будет проще, выспитесь наконец, трава зеленее станет». ну, вы поняли. мотивация какая-то должна быть. про себя скажу — мне лень представлять, мне факты подавай зачем мне про какую-то непонятную штуку писать. за остальных говорить не буду, но одного читателя с таким подходом потереяте:) невелика потеря, но все же.

              так проект ваш, как я понял? а почему не open source не хотите сделать?
                0
                почему же )) я в следующих статьях напишу еще про «зеленую траву»
                кусочек травы я оторвал уже в этой статье

                просто если пытаться сводить какие-то универсальные системы к чему-то одному, что всегда выходит что-то вроде «вот главный принцип, задумайтесь, какие возможности это открывает»

                это как спросить «зачем нужна кибернетика» — и вряд ли создатель скажет, что она была нужна, чтоб вы могли писать сообщения по 140 символов в твиттер )

                так и тут, по сути все множество яваскрипт решений теперь доступно дизайнерам (под тонкой оберткой)
                один пример я привел, приведу еще

                поэтому, надеюсь, что я еще вас не потерял )

                проект выложен на гитхабе
                куда уж опенсорснее?
                github.com/boomyjee/teacss
            +14
            С teacss вы можете сделать графику параметрической тоже

              +1
              не сочтите за рекламу, но может кому интересно будет, в тему данного поста.
              tenphi.github.com/jcss/
              хотя тут конечно без поддержки работы с цветами, там для этого отдельный плагин надо ставить.
                0
                У меня тоже есть подобная библиотечка, которая реализована в разы проще и немного функциональней (поддержка минификации цветов, rgb -> hex -> short hex).
                  0
                  да, я примерно с этого и начинал
                  отказался от подходя из-за нечитаемости JS — у него все-таки не декларативная природа, отсутствия обратной совместимости c СSS
                  ну а так было интересно с вами пообщаться на тему, для каких задач писали вы свой код
                    0
                    ну а так было интересно с вами пообщаться на тему, для каких задач писали вы свой код

                    Мне это нужно было для динамической генерации CSS.

                    Изначально реализация была только на Python, потом сделал версии для JS/CS, на случай что придется генерировать CSS на клиенте (но потом поменяли архитектуру приложения и от этой затеи отказались, хотя генерация CSS на клиенте была довольно шустрая, во всяком случае быстрей чем запрашивать файл с сервера).

                    Что касается формата, то это был не случайный выбор, т.к. синтаксис CSS схож с пиновским словарем ничего парсить не потребовалось (родной формат, простой обход свойст объекта).
                      0
                      а чем SASS/LESS не устроили?
                        0
                        У меня стояла задача сгенерировать CSS код (нужно было отдавать клиенту архив проекта, в котором находился CSS файл с уникальными стилями).
                        Самый простой способ это сделать — представить CSS-структуру в виде объекта (ключ/значение).

                        # Допустим значения будут браться из БД
                        sql = query 'select color, position from WIDGET_SETTINGS'
                        
                        # Валидный объект
                        css =
                        html:
                           body:
                              background: sql.color
                           div:
                              position: if sql.position is 'bottom' then 'left' else 'right'
                         
                        
                        # Добавить в объект новое правило
                        css.span = color: 'red'
                        
                        to = require './toCSS.coffee'
                        
                        # открыть/создать файл на запись
                        file = fs.createWriteStream 'file.css',
                           flags: 'a'
                           encoding: 'utf-8'
                           mode: 0666
                        
                        # Записать CSS в 'file.css'
                        file.write to.toCSS css     
                        


                        Результат:

                        html body {
                        	background: green;
                        }
                        
                        div {
                        	position: left;
                        }
                        
                        span {
                        	color: red
                        }
                        


                        В этом случае, не нужно писать парсер (это значит что работать с CSS можно как с обычным объектом и не нужно придерживаться какого-то специфического синтаксиса).

                        На клиентской стороне генерация CSS кода будет быстрей чем запрашивать файл с сервера или использовать LESS.

                        К тому же, когда я это делал, мне потребовалось всего 30 минут для реализации (больше бы времени ушло на чтение, установку и сравнение документации для SASS/LESS).
                –1
                TeaCSS — CoffeeScript…

                А теперь по делу.
                > Файл tea преобразуется в JavaScript
                И страничка поедет с NoScript.

                > Вы все-таки лишаетесь некоторых сущностей предметной области CSS, таких как «пиксели», «проценты»...
                А вот это зря. Мне давно хочется вычитать пиксели из процентов, и всякие функции типа -moz-calc мне в этом помогают. Раз уж всё равно JS — что мешает добавить то же самое, но кроссбраузерно?
                  0
                  > И страничка поедет с NoScript.
                  Что куда поедет — JS-парсер только для разработки, для продакшена используется серверсайд генерация.
                    0
                    Сгенерируйте мне на серверсайде такой код:
                    div {
                     width: @window.innerWidth - 10;
                    }
                    
                      0
                      ну вот так и пишете, как написали )
                      ну почти
                      div {
                          width: @{$("#other_element").width() - 10}px;
                      }
                      


                      просто в вашем примере вы хотите того, что не умеет в итоге сам CSS (т.е. хотите императивного подхода)
                      и по сути, хотите вещей неправильных (хотя их тоже можно делать на TeaCSS)
                      то, что вы написали, делается padding-ом

                      приведите живой случай или может списаться в личке и я попробую понять, чего вам не хватает и как это можно сделать

                      а teacss все-таки не совсем serverside, он просто не для продакшена, но по время разработки работает в браузере, поэтому фишки вроде вашей возможны
                    +1
                    «Будет тебе и кофе, и какао с чаем!»
                    Нужен ещё CacaoHTML
                      0
                      Ниже уже ответили, все и так кроссбраузерно. Это не решение для продакшена.
                      Лишаетесь — это не значит «не можете использовать», это значит что эти сущности отсутствуют как базовые в JS. Там нельзя переопределять операторы, а по факту запросить width любого дива и его использовать, переводить проценты в пиксели и так далее вы можете.

                      Тут, правда, напрашивается картинка с буханкой-троллейбусом.
                      –1
                      1. Пример про генерацию картинок совсем не понравился, в то время как все пытаются «представление» и «контроллер» разъединить для написания более понятного кода, teacss наоборот объединяет и ещё как пример выставляет. Да ещё и даёт множество возможностей «выстрелить себе в ногу».
                      2. Не вижу преимуществ по сравнению с SASS, не мог бы автор расписать поподробнее.
                        +1
                        1. не совсем уловил, где teacss объединяет эти сущности, наоборот, пытается разделись
                        картинки — часть представления, логично их смешать с CSS, а не открывать каждый раз фотошоп, чтоб увеличить ручками размер или цветовую гамму шапки
                        2. давайте так, я просто не очень пока понимаю, что будет преимуществом именно для вас
                        поэтому я готов попробовать рассказать вам больше (в формате диалога), а потом добавить суть диалога, если она там будет, в статью
                          0
                          1. По сути тут происходит генерация картинки, в моём понимании «представление» генерировать картинки не должно в принципе, оно отображает готовые данные. CSS описывает как документ будет выглядеть и создавать контент для отображения лучше на серверной стороне (ни в коем случае не забывайте о пользователях нетбуков и смартфонов, количество которых растёт всё больше и больше).
                          2. ок, я тогда подумаю как более ёмко изложить и спишемся
                            0
                            1. речь о картинках оформления (бордюры, фоны, шапки) и т.п., т.е. частях шаблона
                            к картинкам, которые часть контента — это не относится
                            2. конечно, всегда рад буду ответить
                          0
                          Как для меня, так плюс пред SASS в том, что можно сгенерировать css файл не устанавливая руби и еще каких дополнительных вещей, обязательно попробую teacss в следующем своем проекте :)
                            0
                            ну с меня тогда ответы на любые вопросы )
                              0
                              ок :) если появятся
                          0
                          Мне понравилось. Только я бы предпочёл иметь линк на стиль в стандартом формате,
                          вместо что-нибудь такое, например:
                          <link rel="stylesheet" type="text/teacss" href="style.tea" />
                          

                          rel — по желанию, type — как маркер типа линка, а href — чтобы можно было использовать как обычный линк с автораспознаванием URL (чтобы можно было открывать из девелопера, чтобы URL подчинялся <base /> при наличии последнего и прочие плюшки для URL)

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое