Как стать автором
Обновить

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

Очень интересный подход! А как передать не булево свойство внутрь?

И более менее сложный лэйаут будет требовать десятка дивов, импорты превратятся в Div as ComponentWrapper.

Я бы посмотрел в сторону чего-то такого:

div#Wrapper { // возможность указать тэг
  color: green;
  
  .dangerous { // булево
     color: red;
  }
  
  background: attr(background); // получаем значение из проперти. к слову, это можно вообще нативно через дата аттрибут сделать, но вроде как там возможности пока что сильно лимитированны 
}

Вроде в рамках нормального css (sass), но гибче.

А как передать не булево свойство внутрь?

Внутрь ничего передавать не нужно, расценивайте это не как улучшение styled-components, а больше как улучшение css-modules, т.е. мы избавились от использования библиотек типа `classnames` и ручной генерации значения свойства `className`, а всё остальное осталось как и было, т.е.

.avatar {
  width: 40px;
  height: 40px;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 50%;
}
import { Div } from './styles.css'

function Avatar(props){
 	return <Div avatar style={{backgroundImage: `url(${props.src})`}} /> 
}

И более менее сложный лэйаут будет требовать десятка дивов, импорты превратятся в Div as ComponentWrapper.

В том то и дело, что всё будет гораздо проще :)

import { Div } from './styles.css'

function Layout(props){
 	return <Div wrapper>
    	<Div header />
    	<Div aside />
        <Div content>{props.children}</Div>
  		<Div footer />
  </Div>
}

при этом в стилях у нас

.wrapper{}
.header{}
.aside{}
.content{}
.footer{}

Синтетический пример, но думаю замысел понятен :)

получаем значение из проперти. к слову, это можно вообще нативно через дата аттрибут сделать, но вроде как там возможности пока что сильно лимитированны

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

style={{backgroundImage: `url(${props.src})`}}

А как передать не булево свойство внутрь?

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

для динамического определения какого-то свойства компонента необходимо использовать подход css-modules, а именно передачу через свойство `style`

спасибо)

получаем значение из проперти. к слову, это можно вообще нативно через дата аттрибут сделать, но вроде как там возможности пока что сильно лимитированны

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

<Div name __fontSize="14px">John</Div>

конвертировать в переменные, получая на выходе

<div class="name" style="--fontSize: 14px">John</div>

доступ к которым получать через `var(...)`

.name {
	font-size: var(--fontSize);
}

что я собственно и сделал

Цсс-модули и стайлед-компоненты хорошо стыкуются с пользовательскими компонентами, у которых есть подпорка className (бывают, правда, ещё пропсы вроде anotherClassName, с которыми стайледы почти не дружат).

Как с этим обстоит дело у сабжа?

Тут всё хорошо :)

<Div root red className="anotherClassName" />

на выходе даст, что-то типа

<div class="_root_78bp722 _red_53ds782 anotherClassName"></div>

т.е. значение свойства `className`, если оно определено, будет проброшено до целевого компонента

Я немного о другом. Вот допустим календарь - кастомный компонент. У него есть пропсы className и tileClassName. Как их задать через Candy?

аа, вот вы о чем )

сейчас это можно сделать, например, так: в файле стилей определить

/* style.css */

:global .calendar_custom_classname {
    color: rebeccapurple;
}

:global .calendar_custom_tile_classname {
    color: red;
}

затем

import './style.css'

function MyApp() { 
  return ( 
    <Calendar 
    	className="calendar_custom_classname" 
    	tileClassName="calendar_custom_tile_classname" /> 
  )
}

можно конечно рассмотреть вопрос о том, чтобы `candy-loader` помимо компонентов-тегов экспортировал ссылки на локальные названия классов, тогда можно будет сочетать подход css-modules, типа

/* style.css */

.calendar_custom_classname {
    color: rebeccapurple;
}

.calendar_custom_tile_classname {
    color: red;
}
import {calendar_custom_classname, calendar_custom_tile_classname} from './style.css'

function MyApp() { 
  return (
    <Calendar 
    	className={calendar_custom_classname} 
    	tileClassName={calendar_custom_tile_classname} /> 
  )
}

тут `calendar_custom_classname` и `calendar_custom_tile_classname` будут содержать что-то типа "_calendar_custom_tile_classname_0ksfd33"

Ок, понятно. То есть по сути возвращаемся всё к тем же цсс-модулям.

Разный подход к встроенным и кастомным компонентам - не очень хорошо (хотя могу ошибаться).

Здорово, мне очень понравилась идея

Если не сложно можно объяснить как работает red={props.red}, то есть он устанавливает класс, если props.red===true или я не правильно понял?

да, вы всё правильно поняли, с использованием css-modules это выглядело бы так

import styles from './styles.css'

function Component(props) {
    let cn = styles.root
    
    if(props.red) cn += ' ' + styles.red
  
    return (
        <div className={cn}>...</div>
    )
}

с использованием, например, библиотеки classnames, так

import styles from './styles.css'
import classnames from 'çlassnames'

function Component(props) {
    const cn = classnames({
      [styles.root]: true,
      [styles.red]: props.red
    })
  
    return (
        <div className={cn}>...</div>
    )
}

всё что делает candy - прячет работу по формированию свойства `className` под капот

import { Div } from './styles.css'

function Component(props) {
    return (
        <Div root red={props.red}>...</Div>
    )
}

во всём остальном на выходе мы получаем тоже что и в css-modules, что-то типа

<div class="root_23sadsd red_32423xad2">...</div>

На компонентах размера побольше хорошо заметна разница в читабельности кода

Для сравнения css-modules:

const Post = (props) => {
    /* ... */ 
    return (
        <div className={styles.post}>
            <div className={styles.cover} style={{ backgroundImage: `url(${cover})` }} />
            <div className={styles.info}>
                <div className={styles.title}>{title}</div>
                <div className={styles.stats}> 
                    <span className={styles.comments}>{commentsCount}</span>
                    <span className={styles.likes}>{likesCount}</span>
                </div>
            </div>
        </div>
    )
}

candy:

const Post = (props) => {
    /* ... */ 
    return (
        <Div post>
            <Div cover __src={`url(${cover})`} />
            <Div info>
                <Div title>{title}</Div>
                <Div stats> 
                    <Span comments>{commentsCount}</Span>
                    <Span likes>{likesCount}</Span>
                </Div>
            </Div>
        </Div>
    )
}

В такой верстке сразу видно и используемые теги, и используемые стили. Это чище на вид и легче для восприятия, если знаешь правила по, которым создан и работает код.

Будем честны - в большинстве случаев при верстке у тегов мы максимум, что определяем - обработчики событий типа `onClick` и свойство `className`. Думаю нет ничего сложного в том, чтобы распознать, что `<Span comments>` это тег `span` со стилем class="comments"`. Откуда этот стиль берется видно по импорту, typescript плагин также подсветит ошибку при её наличии.

но даже тут постоянный бойлерплейт вида className={cn(...)} начинает порядком надоедать

Проблема была решена еще в 2018ом

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории