0,0
рейтинг
14 января 2013 в 18:47

Разработка → В поисках идеального css-фреймворка. Требования, реализация, maxmertkit



Я обожаю twitter bootstrap. Прост, местами логичен, достаточно красив, подходит для быстрого прототипирования веб-интерфейсов. Но этого оказалось недостаточно. Взяв twitter bootstrap в большой проект, мне пришлось целиком его разобрать и переосмыслить css-фреймворки как боевые единицы в веб-проектах. В результате переосмысления родились требования к любому css-фреймворку, удобному как верстальщику, так и frontent-разработчику.


Требования к фреймворку


  1. Для классов фреймворка необходимо использовать неймспэйсы. Это позволяет видеть какой класс принадлежит фреймворку, а какой написали вы. К тому же избавляет от риска переопределения класса фреймворка, из-за которого все может сломаться.
  2. Все компоненты фреймворка, будь это кнопки или выпадающие меню, должны быть независимыми виджетами, при удалении которых фреймворк внешне не пострадает (я молчу про возможность компиляции).
  3. Для внешней модификации фреймворка должен быть отдельный файл с темой, изменив который вы получаете внешне совсем другой фреймворк.
  4. Нужно иметь возможность менять имена классов! Причем делать это в файле темы.
  5. Необходимо избавить frontend-разработчика от необходимости помнить тонну классов для стилизации кнопочек, менюшек, попапов. Здесь под стилизацией я понимаю применение к ним различных состояний и статусов, например error, loading, disabled и т.д.
  6. При добавлении в тему фреймворка какого-нибудь модификатора (расскажу о них позже), например, статуса, все подключеные виджеты должны уметь показывать этот новый статус без правки исходного кода этого виджета. Например добавляем в файл темы новый статус “deepspace”, который имеет цвет фона #000. После чего элементы абсолютно всех виджетов (табы, кнопки, попапы) при применении статуса “deepspace” становятся черными.
  7. При применении модификатора к виджету-родителю, все виджеты-дети наследуют этот модификатор. Например, если мы имеем группу, внутри которой кнопки и текстовые поля, и стоит задача сделать всю эту группу большой и поставить класс “disabled” всем элементам внутри группы. Для этого добавляем модификаторы (классы, например, big и disabled) не к внутренним элементам, а к группе.
  8. Все стандартные иконки – шрифтовые. Это решает очень много проблем.
  9. Необходимо иметь возможность быстро собирать новые виджеты с учетом вышеперечисленных требований.

Прочитав требования, которые сам же и составил, я понял, что таких фреймворков я не знаю. Значит есть над чем поработать.

Краткая история создания


Очень кратко. Я сделал три версии. Первая была сделана дня за три. Естественно, на чистом css такое не напишешь, поэтому для первой версии я выбрал LESS. Сахар оказался не достаточно сладок, и для второй версии я выбрал SASS. Оказалось он гораздо гибче и позволяет делать вещи, которые на LESS реализовать сложно, а порой просто нельзя. Вторую версию я делал около трех недель, но в силу того, что я только начал осваивать SASS, вместо вспаханного поля я получил кратер. Попытка переписать то, что уже есть, все только усугубила, поэтому я приступил к третьей версии, которую вы можете видеть сейчас. Названия я давать не умею, поэтому просто добавил kit к своему никнейму. Сайт – maxmert.com.

Maxmertkit. Структура файлов.


Все файлы ни в коем случае не должны лежать в одной большой куче. Это, в конце концов, не только не красиво, но и не практично.



Classes. Папка содержит все базовые классы, от которых наследуются все остальные виджеты. На данный момент он один – object.
Modificators. Содержит все возможные модификаторы, применяемые к виджетам. На данный момент это статус, размер и другое.
Widgets. Внутри все виджеты, от кнопки до галереи.
Animations. CSS-анимации для виджетов.
_init.scss. Инициализация типографики и базовых классов.
_mixins.scss. Примеси.
maxmertkit*.scss. Файлы для компиляции. По сути они просто подключают необходимые модули. Если вам что-то не нужно, просто закоментируйте импорт.
style.scss. Ваши стили.

Кредо.


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

Неймспейсы.

Для грамотного написания своих классов в фреймворке жизненно необходимы неймспейсы. Для чего? Я думаю частенько возникает желание назвать класс для бейджа badge, а для таблицы – table. Только все время приходится вспоминать все используемые во фреймворке классы. Не совсем практично. В maxmertkit такого недостатка нет.

class=”-{имяВиджета}” – все названия виджетов, например -table, -tooltip, -badge, -modal и т.д.
class=”-{имяСтатуса}-” – имя статуса, например, -error-, -warning-, -info-, -disabled-, -unstyled- и т.д.
class=”_{имяРазмера}” – имя размера, например, _tiny, _small, _big, _huge.
class=”_{именаДругихМодификаторов}_” – например, _loading_, _unclickable_, _active_ и т.д.

Что важно, для любого виджета используются одни и те же модификаторы. Например модификатор _loading_ можно поставить как кнопке, так и таблице или табу. Это позволяет не запоминать длинные составные классы для каждого из виджетов.

Рассмотрим рядовой пример. Есть кнопка, которая удаляет какой-либо контент. Имеется и несколько условий. Сразу после загрузки страницы мы должны проверить, может ли данный пользователь удалять этот контент или нет (например, хватает ли кармы). Причем если не может кнопку все-равно показывать, но со статусом disabled, если может, со статусом error (чтобы кнопка была красной). Пока задача ставится целиком для css. Поэтапно.

  1. Страница загружена, показываем disabled кнопку со статусом загрузки.


    <a class="-btn -disabled- _loading_ -error-">Button</a>


  2. Далее осуществляем проверку на на наличие кармы. Предположим пользователь не может удалять контент. Тогда просто удалим модификатор _loading_.


    <a class="-btn -disabled- -error-">Button</a>

  3. Если кармы все же хватает и пользователь может удалять контент, удаляем модификатор _loading_ и -disabled-.


    <a class="-btn -error-">Button</a>

  4. Если пользователь нажал на кнопку, добавляем модификатор _active_.


    <a class="-btn -error- _active_">Button</a>

  5. Когда началось обращение к серверу, добавим модификатор _loading_.

    <a class="-btn -error- _active_ _loading_">Button</a>


Повторюсь, что продемонстрированные модификаторы работают для всех виджетов, не только для кнопки. На главной странице www.maxmert.com есть небольшой конструктор, позволяющий понять как они работают для других виджетов.

Наследование модификаторов

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

Это группа без модификаторов.

<div class="-group">
   <a class="-btn">I like it</a>
   <a class="-btn">
       <i class="-icon-thumbs-up"></i>
   </a>
</div>

Добавим статус -primary-.

<div class="-group -primary-">
   <a class="-btn">I like it</a>
   <a class="-btn">
       <i class="-icon-thumbs-up"></i>
   </a>
</div>

Добавим статус -dark- кнопке внутри группы.

<div class="-group -primary-">
   <a class="-btn">I like it</a>
   <a class="-btn -dark-">
       <i class="-icon-thumbs-up"></i>
   </a>
</div>

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

<div class="-group -primary- _huge">
   <a class="-btn">I like it</a>
   <a class="-btn -dark-">
       <i class="-icon-thumbs-up"></i>
   </a>
</div>


Промежуточные итоги

Итак, пока мы решили следующие задачи (нумерация соответствует списку в начале):
    1. Используем легко запоминающиеся неймспейсы.
    2. Используем независимые друг от друга виджеты, которые можно легко исключить из сборки.
    5. Нет необходимости помнить много составных классов для разных виджетов, потому что одни и те же модификаторы применяются ко всем виджетам.
    7. Модификаторы наследуются виджетами-детьми.
    8. Шрифтовые иконки.

Остаются пункты:
    4. Возможность легко менять имена виджетов и модификаторов.
    6. При изменении модификаторов (цвета, размера и т.д.), их удаления или добавления все изменения должны быть применены ко всем виджетам. То есть если мы добавили какой-нибудь модификатор статуса, то его можно сразу же применить к любому из виджетов без правки его кода.
    9. Легкая сборка новых виджетов с учетом вышеперечисленных требований.

Файл темы

Файл темы успешно решает задачи 4 и 6. Посмотрим каким образом. Он находится в папке themes. Название этого файла может быть любым, начинающимся с подчеркивания (требование SASS, файлы с такими названиями не компилируются отдельно), только не забудьте импортировать нужный файл темы в проект.

Имена переменных внутри файла темы образуются следующим образом:
${объектРодитель}__{данныйОбъект}-{свойство}__{составнаяЧасть}-{свойство}
Например,

$object__group
object – родитель (как я уже и говорил, пока что он один);
group – это объявляемый виджет.
$object__group__appendix
appendix – это составная часть, находящаяся внутри виджета group (это может быть header, content и вообще все, что угодно)
$object__group__appendix-border-width
Это объявление толщины границы текстового придатка внутри группы.
$object__dropdown__header-padding
Внутренний отступ заголовка выпадающего дропдауна.

Итак, как же объявляются имена виджетов? Очень просто. При объявлении виджета без его свойств всегда указывается название класса. Таким образом, если вам не нравятся названия классов, вы легко сможете поменять их в файле темы:

$object__modal: -modal;
$object__dropdown: -dropdown;
$object__tooltip: -tooltip;
$object__toolbar: -toolbar;
$object__progressbar: -progress;
$object__progressbar__bar: #{$object__progressbar}-bar;


Посмотрим на модификаторы. Для начала разберем модификаторы статуса.



Для каждого из модификаторов выделен отдельный столбец. Верхняя строка – это название. Удалив столбец с модификатором, например, error, мы получим вполне ожидаемый результат: модификатор error исчезнет и ни один из виджетов не будет изменяться при добавлении соответствующего класса -error-.
Кроме того вы можете поменять название, к примеру на error1, и теперь только добавив к виджету класс -error1- вы измените его статус.
Также вы можете добавить столбец со своим статусом и он сразу же будет работать со всеми виджетами.

Теперь о модификаторах размера.



Здесь вы так же можете поменять название модификатора в столбце, изменить множитель размера виджета, удалить или добавить свой размер. Все изменения тоже отразятся на всех виджетах.

Виджеты, имеющиеся в maxmertkit

На скриншотах далеко не все, что умеют эти виджеты. В сочетании с различными модификаторами они смотрятся совершенно по-другому, появляются десятки вариантов одного виджета. Сходите на maxmert.com, посмотрите примеры, полистайте доки.

  • Таблицы, -table
  • Формы -form
  • Иконки -icon
  • Социальные иконки -icon-social
  • Сетка -grid
  • Кнопка -btn
  • Group -group
  • Табы -tabs
  • Бейджи -badge
  • Метки -labels
  • Выпадающий контент -dropdown
  • Меню -menu
  • Меню в dropdown’е (каюсь за повтор, но я не удержался, больно нравится; меню в дропдауне выглядит немного по-другому)
  • Тултип -tooltip
  • Прогрессбар -progress
  • Уведомления -notify, используются совместно с javascript-плагинами
  • Окна -modal, используются совместно с javascript-плагинами

Полный список можно увидеть внутри папки widgets.

Яваскрипт

На данный момент я написал 8 плагинов.
  1. Popup. Для tooltip’ов и dropdown’ов.
  2. Tabs.
  3. Button. Для создания кнопок-чекбоксов и радоибаттонов.
  4. Modal.
  5. Affix.
  6. Scrollspy.
  7. Notify.
  8. Carousel.

Здесь сложно кого-то удивить. В доках все есть. Есть только несколько моментов, о которых я хотел рассказать.
  1. Во многих плагинах в качестве коллбэка beforeOpen, beforeAction, afterOpen и др. (все в доках) можно писать $.ajax (или использовать $.Deferred внутри коллбэка). До тех пор, пока данные не будут получены, плагин не будет предпринимать никаких дальнейших действий (можно получать, например, контент для dropdown’а). Это очень удобно и позволяет сосредоточиться на протекающих процессах, а не на способах их реализации.
  2. Помимо javascript анимации, реализована css анимация. Смотрится, на мой вкус, потрясающе.
  3. Все плагины отслеживают изменение параметров внутри себя. То есть, чтобы изменить тему, нужно просто передать плагину {theme:’dark’}, он сделает все остальное. Скажем “нет” постоянным вызовам методов.
  4. Во многих плагинах есть “бездна”, куда сбрасывается каждый инстанс плагина. То есть из экземпляра плагина у вас есть доступ к другим экземплярам этого же плагина. Чрезвычайно удобно, если, например, нужно закрывать все dropdown’ы, когда открывается текущий экземпляр.
  5. Есть еще что-то, о чем я наверняка забыл.

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

Создание нового виджета

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

Разберем на примере кнопки.
Новый виджет кладется в папку widgets, название файла начинается с подчеркивания (для предотвращения самостоятельной компиляции). Сначала пишутся все стили, не касающиеся модификаторов, уникальные для этого виджета.

button,
.#{$object__button},
input[type="button"]
{
        @extend %__object;
        text-decoration: none;
        cursor: pointer;
        border-width: $object__button-border-width;
        border-style: solid;
        @include border-radius( $object__button-border-radius );
        @include box-shadow( $object__button-shadow );
}

@extend %__object – наследование от класса объект, о котором многократно говорилось выше. Это дает возможность хорошо применять модификаторы для этого виджета.

Все переменные, конечно же, берутся из файла темы.

Далее самое интересное. Мы должны указать, что к виджету “кнопка” применимы определенные модификаторы. Причем когда один из модификаторов применяется к виджету, он не обязательно меняет в нем все от цвета текста до тени, он может поменять в нем только маленькую часть (например при добавлении модификатора -error- к кнопке поменять только цвет текста, а фон и все остальное не трогать). В нашем случае

$__inheritance: object;
$__before-object: '';
$__object: 'button' 'input[type="button"]' '.#{$object__button}';
$__after-object: '';
@include set_modificator($mod__status, color-invert, border-color, gradient-vertical, text-shadow);

$__inheritance: object – класс объекта-родителя (можно задать другой, но об этом немного позже)
$__before-object и $__after-object – о них чуть ниже (но их обязательно указывать даже пустые).
$__object – здесь в кавычках через пробелы или табы указываются объекты, к которым непосредственно будут применяться модификаторы (то есть на которые будут навешиваться классы модификаторов)
@ include set_modificator($mod__status, color-invert, border-color, gradient-vertical, text-shadow) – функция, устанавливающая для виджета модификаторы статуса $mod__status (это те самые столбцы из файла темы), причем при применении одного из модификаторов устанавливаются только инвертированный цвет текста, цвет границ, вертикальный градиент и тень для текста.

Попробуем изменять кнопку, когда пользователь наводит на нее курсор:

$__inheritance: object;
$__before-object: '';
$__object: append-list('button' 'input[type="button"]' '.#{$object__button}', '', ':hover');
$__after-object: '';
@include set_modificator($mod__status, gradient-vertical-darken);


Как вы заметили, здесь вместо обычного списка используется функция append-list, которая просто добавляет ‘:hover’ в конец каждого элемента списка. Мы могли бы не использовать ее, а просто написать

'button:hover' 'input[type="button"]:hover' '.#{$object__button}:hover'

Кроме того компоненты модификаторов, которые использовались ранее, больше не указываются, указываются только новые компоненты, или компоненты, которые переопределят старые (в нашем случае в кнопке с псевдоклассом :hover компонент gradient-vertical-darken переопределит старый gradient-vertical)

То же самое для :active

$__inheritance: object;
$__before-object: '';
$__object: append-list('button' 'input[type="button"]' '.#{$object__button}', '', ':active');
$__after-object: '';
@include set_modificator($mod__status, gradient-vertical-darkener);

Если вам лень писать список в $__object, можно написать вот так:

button,
.#{$object__button},
input[type="button"] {
       $__inheritance: object;
       $__before-object: '';
       $__object: this;
       $__after-object: '';
       @include set_modificator($mod__status, color-invert, border-color, gradient-vertical, text-shadow);
       
       &:hover {
                $__inheritance: object;
                $__before-object: '';
                $__object: this;
                $__after-object: '';
                @include set_modificator($mod__status, gradient-vertical-darken);
       }
       &:active {
                $__inheritance: object;
                $__before-object: '';
                $__object: this;
                $__after-object: '';
                @include set_modificator($mod__status, gradient-vertical-darkener);
       }
}

Вы, конечно, заметили, что блок

$__inheritance: object;
$__before-object: '';
$__object: this;
$__after-object: '';

многократно повторяется. Его можно написать один раз до объявления кнопки.
То же самое без лишнего кода:

$__inheritance: object;
$__before-object: '';
$__object: this;
$__after-object: '';
button,
.#{$object__button},
input[type="button"] {
        @include set_modificator($mod__status, color-invert, border-color, gradient-vertical, text-shadow);
        &:hover {
                @include set_modificator($mod__status, gradient-vertical-darken);
        }
        &:active {
                @include set_modificator($mod__status, gradient-vertical-darkener);
        }
}

А что же делать, если виджет кнопка лежит внутри другого виджета? Вот здесь нам пригодится $__before-object и $__after-object:

$__inheritance: object;
$__before-object: ‘’;
$__object: '.#{$object__group}';
$__after-object: 'button' 'input[type="button"]' '.#{$object__button}';
@include set_modificator($mod__status, color-invert, border-color, gradient-vertical, text-shadow);

Это значит, что модификаторы теперь будут применятся к группе, так как $__object теперь группа, но внешне будет изменяться все, что находится в $__after-object. То есть в результате компиляции мы получим что-то вроде

-group.-error- > button { ... }
-group.-error- > input[type="button"] { ... }
-group.-error- > .-btn { ... }
-group.-info- > button { ... }
-group.-info- > input[type="button"] { ... }
-group.-info- > .-btn { ... }
…
и так для всех статусов.

Если же мы будем использовать $__before-object,

$__inheritance: object;
$__before-object: ‘.#{$object__menu}’;
$__object: '.#{$object__group}';
$__after-object: 'button' 'input[type="button"]' '.#{$object__button}';
@include set_modificator($mod__status, color-invert, border-color, gradient-vertical, text-shadow);

то получим нечто похожее на
.-menu > -group.-error- > button { ... }
.-menu > -group.-error- > input[type="button"] { ... }
.-menu > -group.-error- > .-btn { ... }
.-menu > -group.-info- > button { ... }
.-menu > -group.-info- > input[type="button"] { ... }
.-menu > -group.-info- > .-btn { ... }
…
и так для всех статусов.

Последний не рассмотренный вопрос: что делать, если для данного элемента применимы не все модификаторы (то есть нам нужно исключить из списка $mod__status некоторые из них)?

$__inheritance: $object__group-important;
$__before-object: '.#{$object__group}';
$__object: 'button.#{$mod__loading}' 'input[type="button"].#{$mod__loading}' '.#{$object__button}.#{$mod__loading}';
$__after-object: '';
@include set_modificator(exclude-items($mod__status,$mod__status__disabled, default), loading);

@ include set_modificator(exclude-items($mod__status,$mod__status__disabled, default), loading) – исключает из списка $mod__status статусы disabled и default. То есть когда вы поставите модификатор loading отдельной кнопке внутри группы, он не будет применяться, если у кнопки нет никакого статуса или если статус disabled.

Аналогично работает only-items
@ include set_modificator(only-items($mod__status,$mod__status__disabled, default), loading-dark) – оставляет из списка $mod__status только указанные статусы.

Компоненты, доступные для $mod__status:
  • color
  • color-darken
  • color-invert
  • color-important
  • color-invert-important
  • text-shadow
  • border-color
  • border-color-darken
  • border-color-darkener
  • border-color-lighten
  • border-color-lightener
  • border-color-important
  • background-color
  • background-color-lighten
  • background-color-lightener
  • background-color-darken
  • background-color-darkener
  • gradient-vertical
  • gradient-vertical-darken
  • gradient-vertical-darkener
  • gradient-vertical-three
  • gradient-horizontal-three
  • shadow
  • outline
  • loading
  • loading-dark

Это значит что любой из них вы можете использовать следующим образом
set_modificator($mod__status, {имяКомпонента}, {имяКомпонента}, … )

Компоненты, доступные для $mod__size:
  • line-height
  • line-height-small
  • input-line-height
  • font-size
  • font-size-small
  • padding
  • padding-small
  • padding-big
  • padding-huge
  • input-padding

Используется так
set_modificator($mod__size, {имяКомпонента}, {имяКомпонента}, … )

Даже если вы укажете компонент padding, размер объекта будет меняться в зависимости от модификатора размера, но бывают случаи, когда padding меняться должен, но он должен быть маленьким или большим и меняться незначительно. Именно поэтому появляются компоненты padding-small и padding-big.

В качестве послесловия


Сайт – maxmert.com
Github – https://github.com/maxmert/maxmertkit

Мой английский без практики стал совсем русским, поэтому на сайте вы можете встретить ошибки, неправильные обороты или времена. Очень прошу помочь мне и сообщить, если вы увидите такую бяку. Для этого просто выделяете кусок текста и нажимаете на кнопку «Report an error» в левом плавающем меню. Заранее благодарен.

Я достаточно долго тестировал maxmertkit, но все же если вы заметите баги в верстке, напишите issue в github'е.

Благодарю читателей и всех, кто будет использовать этот фреймворк. Надеюсь облегчить нелегкую работу frontend-разработчиков.

Обновление:
  • Выкачена версия 0.0.2. Исправлены основные ошибки, спасибо всем, кто принял участие. Я продолжаю работу в этом направлении :)
  • Добавлена адаптивная верстка. Теперь достаточно применить модификатор _responsive_ к блоку, внутри которого она должна быть (например к body).


Жду предложений, например, какие виджеты вы бы еще хотели? Ну и, конечно, информацию об обнаруженных ошибках в github'e.


Обновлен до версии 1.0.3!
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (135)

  • +12
    Спасибо Вам за Вашу работу, выглядит отлично! Особенно порадовали эффекты появления popup и dropdown :)
    • +5
      Спасибо, что оценили! Старался. :) Очень надеюсь, что всем пригодится.
      • 0
        Пригодится еще как, после пристального рассмотра — супер круто, неделю назад хотел найти что-то по типу бутстрепа на sass, но вот Zurb Foundaton и Compass как-то сильно завазаны на руби-инфраструктуру и поэтому не рубистам их использовать не очень удобно, большое вам спасибо, думаю, что надо вам помочь =)
      • +8
        Макс, в целом всё достаточно интересно и актуально, но есть несколько спорных моментов. Да, ты сделал вроде бы более гибкий фреймворк, чем тот же бутстрап, и я надеюсь что этот инструмент найдёт свою аудиторию, но за счет того, что твой фреймворк более сложный и трудно модифицируемый, считаю что она будет достаточнго узкой.

        Имею такое мнение, что ты никак не избавил верстальщика или фронтенд разработчика от разбирания по-кускам и переваривания фреймворка, ты его более усложнил. И могу сказать исходя из своего опыта – основная твоя идея с общими (одинаковыми) модификаторами чревата нудным последствием – рефакторингом.

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

        Например:

        .-menu > .-group.-error- > input[type="button"]

        Мне кажется этот подход достаточно избыточен и неудобен. Если мы захотим применить эти же стили для ссылки или для инпута другого типа нам придется, как минимум, дописывать через запятую строку селекторов. В том случае, когда у нас изменится расположение кнопки в дом дереве этот вариант не подходит, то есть тут нет ни капли универсальности. Здесь достаточно было бы иметь один уникальный класс у кнопки и разные состояния при наличии различных модификаторов. Тут тебе нет привязки к расположению в дом дереве (имею в виду внутри какого-либо блока), и стили можно применять практически к любому тегу.

        Затем, поизучал maxmert.com/widgets#grid. Если посмотреть на html-код и стили для него, то у меня волосы на голове начинают подниматься – зачем так извращаться над обтеканием, если семантически правильно для таблиц использовать тег table? У нас тут на совсем подходе flexbox ну или на самый крайний случай всё это можно обыграть inline-block.

        Идем дальше.

        <div class="-group">
           <a class="-btn">I like it</a>
           <a class="-btn">
               <i class="-icon-thumbs-up"></i>
           </a>
        </div>
        

        В одном из самых первых примеров кода этой статьи я заметил как минимум 3 “недочёта“:
        1. Использование тега <a> без указание аттрибута href несемантично;
        2. Не вижу смысла использовать тег <i> непоназначению, напомню что он придумывался для оформления текста, по-дефолту делает его курсивом;
        3. Не смог найти смысла разделения, казалось бы, единой кнопки на две, причем одна из них никак не функционирует (см. пункт 1);

        Всё это можно было бы обыграть более изящно и семантично:

        <div class="-group">
           <a class="-btn -thumbs-up-" href="http://www.url.com">I like it</a>
        </div>
        

        и в стилях оперировать:

        .-btn {
        /* стили кнопки */
        }
        .-btn.-thumbs-up- {
        /* стили кнопки с этим модификатором */
        }
        .-btn.-thumbs-up-:before {
          content: url(../img/thumbsup.png);
        }
        


        Могу сделать вывод. Если бы я, в коем-то веке, для какого-нибудь из проектов взял за основу какой-нибудь фреймворк, я бы остановился на бутстрапе.
        • +1
          Спасибо за конструктивную критику. Постараюсь ответить по порядку.

          Рефакторинг будет не нужен по одной простой причине – наличие неймспейсов. Создавайте свой класс, навешивайте на элемент, перемещайте по dom-у, все будет в полном порядке. То, что input[«button»] выглядит в toolbar-е немного по-другому, чем просто в dom-е, так и было задумано. Если вас не устраивает внешний вид, можно поменять тему или исправить виджет toolbar-а. В группе кнопка должна тоже выглядеть немного по-другому (бордер-радиус, например), и как вы это обойдете, не использовав .-group > .-btn?

          На счет грида вы правы, есть узкие места, исправлю обязательно!

          А вот это

          <div class="-group">
             <a class="-btn -thumbs-up-" href="http://www.url.com">I like it</a>
          </div>


          плохо, потому что верстальщику все понятно, а вот программисту тут не понятно ничего. С какой стати перед «I like it» должна появиться еще одна кнопка с иконкой? И как именно ей поменять статус? Например поставить -disabled-? На счет несемантичности без href: ну что вы, тут же просто пример. Тег <i> много где используется для иконок, это очень удобно и, на мой взгляд, достаточно логично (i — icon) :)
    • +4
      Действительно, очень круто!
    • 0
      Ну даешь! Молодчага!
  • +2
    SASS. Оказалось он гораздо гибче и позволяет делать вещи, которые на LESS реализовать сложно, а порой просто нельзя

    Интересно… Например?
    • +1
      Примесь, определяющая от кого наследуется модификатор, при генерации css учитывающая полный путь для текущего виджета.
      @mixin __set_modificator-type( $type, $list, $mod ) {
      	@each $status in $list {
      		@if $__object == this {
      			$__name: _get-modificator-name( $status );
      			&#{$__name} {
      				@extend %#{$__inheritance}-#{$type}-#{$status}-#{$mod};
      			}
      		}
      		@else {
      			
      
      			@each $_obj in $__object {
      				@each $_before in $__before-object {
      					@each $_after in $__after-object {
      						$_nesting: _get-modificator-nesting( $status, $_before, $_obj, $_after );
      						#{$_nesting} {
      							@extend %#{$__inheritance}-#{$type}-#{$status}-#{$mod};
      						}
      					}
      				}
      			}
      		}
      	}
      }
      • 0
        А есть ли возможность в виндоуз поставить что нибудь по -типу wenLess, что бы не возиться с Ruby
        • 0
          Я с руби, к сожалению, тоже не работал. Это sass синтаксис (если я вас правильно понял).
          • 0
            Я имею в виду, что для работы с Sass нужен Ruby sass-lang.com/tutorial.html

            Есть вот такое AIR приложение www.impressivewebs.com/sass-on-windows-with-scout-app/ — я не пробовал, может Вы подскажете, как Вы работает под Windows с SASS — их же надо компилировать. Для LESS у меня стоит WwinLess winless.org/ — который компилит файлы при изменении. Это довольно удобно.
            Плюс есть возможность работать с less.js для компилияции в браузере на лету.
            • 0
              К сожалению, ничем помочь не смогу: всю разработку веду на mac :(, поэтому софта для компиляции sass не знаю. В скором времени собираюсь сделать конструктор, который будет прямо на сайте собирать фреймворк, но он, ясное дело, полноценную разработку не заменит.
              • 0
                > на mac :)
                FTFY
              • 0
                ну значит у Вас стоит руби — не то что бы это большая проблема для меня — он и на виндоуз ставится без проблем, просто не хотелось бы заморачиваться. Спасибо за ответ.
            • +1
              Посмотрите Scout
            • 0
              Не обязательно руби. Как минимум есть форки на PHP и JavaScript, и Python
              • 0
                php и javascript по-моему в альфах до сих пор. Но интересно. Хотя как я понял проще тот же скаут поставить
            • 0
              Вариант поддержки компиляции SASS для работающих в Visual Studio 2012 —
              visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a
        • 0
          Есть еще Livereload, хотел на него перейти. У него компиляция не основная функция, есть еще обновление стилей в браузере без перезагрузки, но он постоянно падал с ошибкой. Попробуйте, возможно у Вас заработает.

          А я вернулся на Scout.
        • +4
          «Возиться с руби» это громко сказано :)
          Сам никогда не юзал руби, но когда понадобилось использовать SASS был удивлен насколько это просто и занимает пару минут от силы.

          Порядок действий таков:
          1) Скачиваете и ставите руби, пути для винды прописываются автоматом, так что все сразу доступно из консоли
          2) Открываете консоль (Win+R -> cmd -> Enter) и ставите SASS: gem install sass
          3) Всё готово к работе! В проекте создаете 2 папки scss и css и запускаете команду sass --watch scss:css в контексте этой директории. Теперь все изменения в SASS-файлах автоматом отслеживаются и генерят новый CSS.
        • 0
          Для Visual Studio есть Web Essentials, но в бесплатной студии не взлетит
        • +2
          Присоединяюсь к комменту выше по поводу «возиться». Наоборот, только удовольствие получите, и, если не сталкивались ранее, научитесь :)

          Ровно как и для LESS — что может быть проще и приятнее, чем запустить node.js на dev-машинке!
          • 0
            а node.js при чем тут?
            Ну и тогда еще вопрос по поводу SASS: а каким форком Twitter Botstrap в виде SASS пользоваться.
            • 0
              node.js нужен для компиляции на сервере
            • 0
              Вот этот хорош github.com/jlong/sass-twitter-bootstrap
              • 0
                Спасибо, видел -но там несколько есть вариантов — буду пробовать этот
                • 0
                  Не просто так посоветовал именно этот. Тоже искал, тоже видел несколько, этот оказался самым актуальным и постоянно обновляемым.
  • +2
    Почему-то не работает «Drop a widget here». Linux, Chrome 25.0.1364.29 dev
    • 0
      К сожалению пока нет возможности тестировать на линуксе. Постараюсь завести виртуалку. Если есть javascript-ошибки, напишите их в github, пожалуйста.
      • +1
        Готово.
        • 0
          Благодарю! )
      • 0
        В сафари, кстати, тоже не работает. Точнее, не работывает скидывание модификаторов и состояний.
        • 0
          Спасибо, посмотрю. На Linux?
          • 0
            Safari, как ни странно, есть только для mac :) На linux не знаю, могу завтра посмотреть в хроме, фф, опере.
            • 0
              :) Был бы очень благодарен! В ближайшие два дня попытаюсь поднять виртуалку.
            • 0
              Для Windows тоже есть Safari.
              • 0
                Уже нет. Его перестали обновлять.
      • 0
        В линуксе, но хром стабильный 24.0.1312.52 — работает
  • 0
    Классная работа, не хватало такого. Хорошо, что все модульно, можно использовать в готовых проектах.
    В «Scaffolding» ожидал увидеть описание сетки, но перешел на Typography (не очень приятно).
    • 0
      Спасибо. Н-да, недочет. Сегодня ближе к вечеру перенесу.
  • +1
    На ipad верстка сайта расползается.
    • 0
      Да, действительно. Спасибо, поправлю.
  • 0
    Вот бы эту штуку да под GWT!
  • 0
    Стрелки у поповеров и прогрессбары — шик и няшка! Настолько приятно, что даже странно, почему я нигде раньше этого не видел. Но остальные вещи местами сыроваты, для своих проектов я бы не стал брать, хотя бутстрап меня явно не устраивает чрезмерной «воздушностью», но ритм у него выдержан лучше.
    • +5
      Спасибо! Версия 0.0.1. :) Планов по оптимизации и улучшению очень много.
  • 0
    Наверняка многие части вашей работы, не расходящиеся в идеалогии с оригинальным бутстрапом, можно законтрибьютить и туда. Вот это было бы действительно очень здорово!
  • +2
    Безусловно можно. Много на less, конечно, переписать нужно. Откровенно говоря, сначала я так и хотел сделать. :) Но у меня в планах пока нет. )
  • 0
    В опере 12.12 на сайте не дропаются элементы в специально отведенное поле :(
    • 0
      И еще социальные иконки смещены к верху кнопок
      • 0
        Да, спасибо. Исправлю.
  • +2
    Кстати, по поводу шрифтовых иконок — я бы не был столь категоричен, восхваляя их.

    У этого подхода среди прочих есть один огромный, фундаментальный и непобедимый недостаток: в маленьких размерах они практически всегда выглядят ну очень ущербно в сравнении с растровыми, результат совершенно неприемлем, если нужны иконки, скажем, размера 16х16 или 12х12 пикселей (как это почти всегда и бывает).
    • 0
      Тем не менее сейчас их используют все больше. Бутстрап, на сколько я знаю, собираются пересаживать на них.
      Тем более в файле темы есть возможность быстро и просто вставить спрайт с любыми своими иконами, если не устраивают шрифтовые.
      • +1
        На маке, вероятно, со шрифтами ситуация получше, но в винде иконки выглядят действительно ужасно. Для хрома есть небольшой трюк. Если в font-face указать путь к svg-шрифту раньше, чем к woff, то ситуация заметно улучшается:



        Возможно, вам это пригодится.
        • 0
          Благодарен! Обязательно исправлю.
        • 0
          раньше это работало — сейчас уже вроде бы нет с версии 23 — судя по этим комментам stackoverflow.com/questions/4060607/font-face-anti-aliasing-on-windows-and-mac#comment17730929_9041280
          • 0
            Я делал скриншоты для этой картинки в последней версии хрома.
            • 0
              Попробовал на одном проекте, стало чуть лучше, но не до конца. В FIReFox и IE значительно лучше
            • 0
              image — с SVG
              image — без SVG
              image с -webkit-text-stroke: 0.5px;
              • 0
                сорри click2net не проплатил
                image с -webkit-text-stroke: 0.5px;
                image без SVG
                image с SVG

                Как видим разницы особой нет между SVG и без SVG — лучший вариант с web-kit-stroke
    • 0
      Есть свойство -webkit-font-smoothing, работает только в webkit, будем надеяться, что разработчики остальных браузеров догадываются о существовании сей проблемы и пытаются ее решить. А так-же есть приемы немного улучшающие ситуацию.
      • 0
        по-моему оно уже не работает — посмотрите я выше оставил пример с web-kit-stroke — лучший трюк на данный момент
        • 0
          Собственно я к тому, что решения с улучшением есть и все движется в лучшую сторону с иконошритфами.
  • +2
    1. 1200px по-моему много. Мой браузер обычно больше 1000 не расширяется.
    2. Посмотрите на stylus. Очень мне понравился после less и sass/compass. Написал небольшой mixin для него, теперь responsive-сайты реализую на раз:
    .logo
        responsive width 500px 360px 300px
        responsive height 170px 122px 102px
        responsive margin, 50px auto 70px, \
                           40px auto 50px, \
                           20px auto 10px
        responsive image "logo" "logo-tablet" "logo-phone"
        svg "logo"
        hide-text()
    
    • 0
      Спасибо! Stylus, к сожалению, не трогал. Посмотрю, вдруг позволит реализовать аналогичные вещи более просто.
      На счет сетки вы возможно правы. Соберу статистику, исправлю. )
      • 0
        +1 за стилус, ещё и от руби уйдёте, что для некотрых может быть «идеологической» сложностью.
  • +3
    После бутстрапа смотрится отлично, спасибо!
  • 0
    Здорово. Вы молодцы!

    PS: Tweet me ;)
    • 0
      Елки-палки… Спасибо! Смотрел на это слово, вроде бы что-то не так. Поленился посмотреть в словаре. ))
  • 0
    Какие инструменты использовали в разработке помимо упоминаемых LESS и SASS?
    • 0
      LESS я не использовал (для финальной версии :) ). Grunt – для сборки проекта, для сайта пришлось писать свой таск для сборки mustache-темплейтов в одну переменную. Coffeescript – для сайта, и сейчас переписываю плагины. Nodejs – сайт. Mongo – бд на сайте. Вроде ничего не забыл.
      • 0
        deleted
        • 0
          :)
  • 0
    В Сафари режется меню в левой колонке. Первые буквы не видны.
    • 0
      А какое у вас разрешение?
      • 0
        макбук айр, наверное 1440 на 900, окно чуть-меньше экрана, сафари 6.0.2
        • 0
          Благодарю! Посмотрю, исправлю.
  • 0
    Круто, автору спасибо за проделанную работу. Нужно попробовать где-нибудь на рельсовых пет-проектах.
  • 0
    С ув. автором поговорили раньше, чем я успел коммент дописать (на issue ответил через гитхаб) ))))

    Приятно видеть ))
  • 0
    С документацией вопрос проясните пожалуйста. На github написано, что она есть на сайте, но я там её нигде не нахожу. Возможно, плохо смотрю. Если так, дайте ссылку плз. или просто скажите, где она (ссылка).
    • 0
      Черная панелька вверху страницы. Ссылка на начало документации: http://maxmert.com/creed.
  • +1
    Концепция действительно лучше чем у бутстрапа. Из того, что я видел, это первая жизнеспособная альтернатива.
    Но для того, чтобы начать ее использовать в бою не хватает двух вещей: стройного дизайна и множества форм (select'ов нет в документации и сгруппированные инпуты).
    • 0
      Спасибо! Был бы очень признателен, если бы вы в гитхабе написали чего нехватка и каким вы видите цельный фреймворк, который готовы использовать в проектах.
  • +2
    Учитвая что Ваш фреймфорк могут использовать другие люди, хочу поинтересоваться чем обусловлено присвоение -webkit-font-smoothing: antialiased; для body, просто не понимаю зачем люди переопределяют нативный рендеринг шрифтов операционной системы?
    UPD: да, знаю что оно помогает шрифтовым иконкам, но ведь читабельность шрифтов портится, же
    • 0
      Скорость рендера не сильно страдает, а выглядит, на мой взгляд, гораздо симпатичнее. Шрифты в невебкитовских браузерах выглядят немного по-другому, но это, на мой взгляд, вполне приемлемо (как cleartype в свое время). Если можно сделать красивее, лучше сделать красивее. =) Но возможно вы знаете о каких-то подводных камнях этого свойства, о которых не знаю я?
      • +2
        Ну как бы подводное свойство у него одно — не всем нравится и сама идея его неправильна и граничит с багом: если у меня в системных настройках как бы стоит галка для включения субпиксельного сглаживания и мне так нравится, то почему сайт отображается без субпиксельного сглаживания? Раньше как-то не обращал внимания, но вот это свойство в последнее время почему-то начали эксплуатироваться по полной. Просто оно действительно не всем нравится, лично мне на макоси просто тяжелей читать становится — у текста падает контраст, потому что это grayscale сглаживание — если присмотреться к увеличенным символам то можно увидеть как, например, если цвет текста черный, а фона — белый, то половина пикселей серая, т.е. с заниженной яркостью и тем самым, контрастом, в то время как при субпиксльном сглаживании, субпиксели сохраняют яркость и контраст шрифта.
        • 0
          Спасибо за развернутый ответ. Обращу более пристальное внимание на данное свойство. Мне показалось, что текст более аккуратным становится, да и при чтении больших текстов проблем не замечал. Попробую отключить, сравнить. Еще раз спасибо.
          • +2
            Спасибо за понимание =) моя позиция простая — если, мне тоже нравился бы такой текст, я бы убрал галку в системных настройках и нет проблем — на всех сайтах были бы такие же шрифты, но вот я ее поставил, а на многих сайтах шрифты остались как если бы сглаживание отключено, т.е. как будто веб мастер принимает решение за меня =(
            • 0
              Добавил issue. )
      • +4
        Насчёт этого свойства и почему его не стоит испоьовать, лучше почитать здесь — www.usabilitypost.com/2012/11/05/stop-fixing-font-smoothing/
        • 0
          О, данке! Интересно. :)
  • 0
    Спасибо всем, кто пишет об ошибках в тексте в багрепорт на сайте. Ответить лично там возможности нет. Я все обязательно исправлю. :)
  • 0
    Очень понравилось!

    Scaffolding > Typography > Headers > «All headers from h1 to h2 are available». Мне кажется, должно быть «до h6».
    Вы программист?
    • 0
      Да, вы, конечно, правы! Спасибо! Скоро выкачу исправленный вариант.
      Да, программист. )
      • +1
        Только программисты пишут _xxx, _xxx_, -ххх-, и, о ужас __хх__ и у них это не вызывает паники ;)
        • 0
          :) Ну, я еще и верстальщиком, и дизайнером долгое время работал. :) Легкие приступы паники верстальщика и дизайнера во мне пропадают, если «xxx» заменяется на что-нибудь понятное и человеческое. :)
  • +1
    Кстати, название напомнило вот этот мем :)

    Скрытый текст
  • 0
    Большое спасибо за данный фреймворк.
    На сайте был найден небольшой баг. Когда перемещаешь размер на элемент и справа изменяется html, я заметил, что размер дописывается в конец, а не производится его замена на другой, при нескольких применений:

    <a class="-btn _huge _small _big _tiny">Dropdown
        <i class="-caret"></i>
    </a>  
    <div class="-group _tiny">
        <a class="-btn">Dropdown</a>
        <a class="-btn">
            <i class="-caret"></i>
        </a>
    </div>
    
    • 0
      Благодарю! Да, перетаскивать «clear sizes» каждый раз не очень удобно. Исправлю! :)
  • +8
    Все красиво, но вот префиксы и постфиксы, по моему, не совсем удобны.
    class=”-{имяВиджета}” – все названия виджетов, например -table, -tooltip, -badge, -modal и т.д.
    class=”-{имяСтатуса}-” – имя статуса, например, -error-, -warning-, -info-, -disabled-, -unstyled- и т.д.
    class=”_{имяРазмера}” – имя размера, например, _tiny, _small, _big, _huge.
    class=”_{именаДругихМодификаторов}_” – например, _loading_, _unclickable_, _active_ и т.д.

    Да они лаканичны, но не самоописательны.

    Что-то вроде такого, было бы более стройно, что ли:
    widget_{имяВиджета}, status_{имяСтатуса}, size_{имяРазмера}, и mod_{именаДругихМодификаторов}.

    Может это и длиннее, но сразу понятно о чем речь и не нужно запоминать какое подчеркивание или минус за что отвечат.
    • 0
      Да, я думал над таким вариантом очень долго. На самом деле после пятнадцати минут использования вы уже не будете вспоминать какое подчеркивание за что отвечает, а вот время экономить будете. :)
      • +7
        Зато, если потом пару месяцев не использовать, то нужно будет снова вспоминать где какой знак. Да и сам факт того что у Вас сначала нужно объяснить какой префикс суффикс для чего. В моем случае это мжет вообще не потребоваться.
        А с учетом автоподстановок, экономия времени на наборе — несущественна.
        • 0
          Спасибо! Подумаю над неймспейсами.
          • +1
            Предлагаю использовать префиксы типа w- или s-
      • +5
        Имхо, статус, размер, именаДругихМодификаторов можно собрать под одно знамя Модификатора. То, что это модификатор именно статуса или размера, должно подсказать само используемое слово, напр., warning, big,…
        Зачем наделять смыслом набор подчеркиваний и дефисов, когда смысл уже заложен?.. К тому же, комбинации, причем двойные, "-" и "_" вносят в исходники хаотичный вид.
        <div class="_big -warning- _unclickcable_ -toolitip">...</div>
        

        Пусть все будет лаконично и однообразно:
        <div class="-tooltip -big -warning -unclickable">...</div>
        

        А то, может, даже так:
        <div class="tooltip -big -warning -unclickable">...</div>
        


        Так, просто предложение…
        • 0
          Спасибо! Да, к неймспейсам есть замечания. Посмотрю, что можно сделать :)
          • 0
            Я честно говоря не понял, почему модификаторам размера отведено особое место. Почему не считать их такими же модификаторами как и все остальные? По собственному опыту я бы не сказал что модификатор размера используется чаще, чем другие. Зачем же различный синтаксис.
  • 0
    Понравилось. Попробую использовать в проекте на Друпал, пока смешиваю Bootstrap и 99Lime.com HTML KickStart by Joshua Gatcke из-за красивых кнопок
    Также поглядываю на Kickstrap ajkochanowicz.github.com/Kickstrap/

    Сайт с демо понравился)
  • 0
    Спасибо! Интересно) Неплохая альтернатива Бутстрапу получается.

    При drag'n'drop в Chrome 24 под Ubuntu появляется следующее:
    Uncaught TypeError: Cannot read property 'nodeName' of undefined (global.js:157)
  • 0
    Мне кажется что меткам больше tags название подойдет. Label у меня всегда ассоциируется с инпутами. А так, отличная работа. Ну и с неймспейсами согласен, упростить можно :)
  • 0
    Has namespaces. You classes will not cross with maxmertkit's classes.

    You -> Your

    Но я бы написал что-то типа «Has namespaces. No more class names conflicts!»
    • 0
      Спасибо! Исправлю! :)
      • 0
        Может все же так:
        By having namespaces you will never fall in to conflicts of overlapped names.

        Ну и: Has namespaces некорректно, тогда уже Have namespaces.
        • 0
          Да, спасибо!
  • +1
    Ставьте кнопку «donate» — с удовольствием пожертвую 20-30 енотов на дальнейшую разработку.
    • 0
      Уже есть. :) maxmert.com/creed
      • 0
        Уже перевёл :)
        • 0
          Спасибо большое! :)
      • 0
        Тоже отправил. Надеюсь, проект не стухнет. Так держать!
        • 0
          Спасибо!
  • 0
    А на русском языке не планируете сделать сайт?
    • 0
      Пока нет. К сожалению нет времени.
  • 0
    Круто!
  • 0
    Спасибо! Дейтствительно, фреймворк радует своей обстоятельностью.
    Особняком стоит вопрос о поддержке. Есть ли на этот счет какие-то соображения и утешения для сообщества? Планируете ли вы популяризировать продукт? А то, кто бы что ни говорил, но Бутстрап в первую очередь популярен благодаря тому, что перед ним стоит слово Твиттер — это вселяет надежность и доверие)
    • 0
      Habrahabr Maxmertkit?
    • 0
      Да, популяризацией планирую заняться. План на ближайшее время таков:
      1. Сделать онлайн-сборщик с возможностью мгновенного просмотра результатов сборки.
      2. Сделать каталог виджетов
      3. Сделать менеджер пакетов для быстрой установки виджетов (нечто вроде npm), с возможностью добавления своих виджетов в каталог.

      Не хочется выезжать за счет других проектов. С помощью сообщества хочется сделать хорошую вещь, которая будет прекрасна сама по себе.
      • 0
        отличный план для развития, хорошая платформа для ui на frontend. — это то что нужно
        • 0
          Спасибо! Мне тоже такого не хватает. )
  • –1
    Я за кофе с сахаром.

    Не смотрели ли в сторону TypeScript?
    Хотя это дело вкуса, IMHO. Но TypeScript поближе к JavaScript будет в плане синтаксиса.
  • 0
    Пожелание: добавьте еще классы для вывода изображений. А-ля thumbnail в bootstrap.
    • 0
      Добавил issue. Спасибо!
  • 0
    Еще стоит добавить хотя бы еле заметный верхний border для кнопок. Иначе с appendix и в группе с input они смотрятся странно — визуально элементы получаются разной высоты.
    • 0
      Да, благодарю, возможно стоит сделать это по-умолчанию. Добавил issue. Если это необходимо, в файле темы это можно быстро поменять.
      • +1
        Спасибо за быстрые ответы! Надеюсь, со временем это у вас не пройдет :)
  • 0
    Охерительно!

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