Giter VIP home page Giter VIP logo

reshadow's Introduction

reshadow ⛱️

Github Actions Coverage Status branch npm version Babel Macro

Markup and styles that feel right.

Please check the reshadow.dev website to get more information and examples.

reshadow provides the Shadow DOM developer experience, but for the Virtual DOM with the Component way.

import React from 'react';
import styled, {css} from 'reshadow';

// Write styles:
const styles = css`
    button {
        width: 200px;
    }
    content {
        font-size: 14px;
    }
`;

export const Button = ({children, ...props}) => {
    // connect the styles to the markup:
    return styled(styles)(
        <button {...props}>
            <content as="span">{children}</content>
        </button>,
    );
};

This project has alpha status, so the API and the implementation could be changed.

Features

  • Get away from additional abstractions
    • Write isolated semantic styles for the Virtual DOM in a native like way
    • Match styles on the elements, components, and props. That's all you need
  • Compile-time styles processing and efficient runtime
  • Static styles extracting options
  • Static analysis
  • Combine the css-in-js and css-modules approaches or choose which fits you better
  • All the benefits of the PostCSS ecosystem
  • Interoperable. Use it with components in React, Preact, Vue, htm.

There are some examples on the Сodesandbox:

Benchmarks

There are also some css-in-js benchmarks, that are available on the codesandbox.

reshadow benchmarks

Docs

npm i --save reshadow

.babelrc

{"plugins": ["reshadow/babel"]}

Check the Setup page to get more details (including setup with Create React App)

Example

import React from 'react';
import styled from 'reshadow';
import {readableColor, rgba} from 'polished';

const Button = ({
    bgcolor = 'lightgray',
    size = 's',
    children,
    ...props
}) => styled`
    button {
        cursor: pointer;
        padding: 5px 10px;
        border-radius: 5px;
        border: 2px solid ${bgcolor};
        background-color: ${rgba(bgcolor, 0.7)};
        color: ${readableColor(bgcolor)};
        transition: background-color 0.5s;

        &:hover {
            background-color: ${rgba(bgcolor, 0.9)};
        }
    }

    /**
     * Match on the 'disabled' prop,
     * not the DOM attribute
     **/
    button[disabled] {
        opacity: 0.5;
        pointer-events: none;
    }

    /**
     * Match on the 'use:size' prop
     */
    button[use|size='s'] {
        font-size: 12px;
    }

    /* The 'use' namespace can be omitted */
    button[|size='m'] {
        font-size: 14px;
    }
`(
    /* use:size property would not pass to the DOM */
    <button {...props} use:size={size}>
        {children}
    </button>,
);
const Container = () => styled`
    Button + Button {
        margin-left: 10px;
    }
`(
    <div>
        <Button size="m" bgcolor="lightgray">
            lightgray
        </Button>
        <Button size="m" bgcolor="orange">
            orange
        </Button>
        <Button size="m" bgcolor="rebeccapurple">
            rebeccapurple
        </Button>
    </div>,
);

buttons

Usage

css-modules

Button/index.js

import React from 'react';
import styled from 'reshadow';

import styles from './styles.css';

export const Button = ({size, children}) => styled(styles)(
    <button use:size={size}>{children}</button>,
);

Button/styles.css

button {
    /* button styles */
}
button[|size='m'] {
    /* button styles for the size */
}

css-in-js

import React from 'react';
import styled, {css} from 'reshadow';

const anotherStyles = css`
    button[disabled] {
        /* disabled button styles */
    }
`;

export const Button = ({size, children}) => styled(
    props.another && anotherStyles,
)`
    button {
        /* button styles */
    }
    button[|size='m'] {
        /* button styles for the size */
    }
`(<button use:size={size}>{children}</button>);

Setup

Macro

With CRA 2 (Create React App) or babel-plugin-macros usage you can just use reshadow/macro out of the box.

import React from 'react';
import styled from 'reshadow/macro';

export const Button = ({children}) => styled`
    button {
        /* button styles */
    }
`(<button>{children}</button>);

Options (via babel-plugin-macros config) are the same as reshadow babel options, but with different defaults:

option default value
postcss true
files /.shadow.css$/

Babel

Add reshadow/babel to the plugin list.

babel.config.js

module.exports = {
    plugins: ['reshadow/babel'],
};

Options

option type default value description
postcss boolean | {plugins: Plugin[]} false Use PostCSS to process CSS code. You can add your custom PostCSS plugins (they should be sync)
files boolean | RegExp false Resolve and process css files imports that match to the RegExp
elementFallback boolean | string 'div' Use fallback for the custom elements

PostCSS

Add reshadow/postcss to the plugin list.

postcss.config.js

module.exports = {
    plugins: ['reshadow/postcss'],
};

Webpack

Use reshadow/webpack/loader to extract styles in separate files.

webpack.config.js

{
    test: /\.js$/,
    use: [
        'reshadow/webpack/loader',
        'babel-loader',
    ]
}

Linting

Use reshadow/eslint if you want to have more control about reshadow usage.

Rules:

Prettier

Use reshadow/prettier if you want to improve your Developer Experience with prettier.

prettier.config.js

module.exports = {
    plugins: ['reshadow/prettier'],
};

Special Thanks

reshadow's People

Contributors

antonk52 avatar dependabot[bot] avatar ilyalesik avatar lttb avatar ne4to777 avatar quasiyoke avatar tamik avatar wroud avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reshadow's Issues

DX-friendly selectors names

I propose to add a component (for css-in-js API) name to generated selector name for development mode for better DX.

For example:

export const Comp = styled`container{...}`(<container />)

Must to generate something like Comp_container_HaCe3q.

the overlying style is not redefined

hi,
if you are not tired of my questions, then here's another:
(ver 0.54 or ver 0.57)
the overlying style is not redefined, if there is a new style below:

const isBadge = ({ badge }) =>
  badge &&
  css`
    ${commonStyle}
    border-radius: 1rem;
    background-color: ${({ color }) => color};
  `;

export const Span = styled.span`
  margin-left: 14px;
  text-align: center;
  box-sizing: border-box;
  float: right;
  line-height: 22px;
  position: relative;
  min-width: 3rem;
  padding: 0 6px;
  font-size: 1rem;
  height: 22px;
  color: #757575;
  background-color: inherit;
  ${isBlock};
  ${isBadge};
`;

background-color: inherit;
always, regardless of the presence of a variable badge=true and color=some color,
and in styles there is a variable, but not applicable

Clarify the relationship to Shadow DOM standard

Hello. I took a look into your project and the positioning is confusing. Especially, using "shadow" makes an impression of something close to Shadow DOM.

But in fact, there are only few statements which I was able to find. Let me elaborate a bit more on how I see your position as a developer and what impression does it make.

Position

The first sentence in the description says the following:

reshadow provides the Shadow DOM developer experience

At the same time, in "motivation" section there is a vague critique:

We also have Shadow DOM, which can fix some kind of that problems like styles isolation, but there are also some tradeoffs, and it often doesn't fit to the requirements.

And the key statement, to my understanding, is the following one:

It is possible to write HTML and CSS (...) like there is nothing else than just the Component.

Impression

Let me wrap this up in a following way (and please correct me if I'm wrong):

  1. You acknowledge that there is a standards-based way of CSS encapsulation

  2. You agree that using Shadow DOM in certain cases has positive sides

  3. You create yet another solution relying on global CSS, and try to mimic the way of how users write CSS when using Shadow DOM, because native way "doesn't fit to the requirements."

Summary

I would really appreciate if you expressed a more clear position related to Shadow DOM:

  1. What exactly tradeoffs does it have for your project? (i.e. having to bundle CSS with JS sounds like a valid excuse to have global CSS, until we have native CSS ES modules)

  2. What exactly are your requirements that Shadow DOM doesn't meet? (i.e. having to support IE11 or let's say old iOS versions would be a valid excuse as well).

My only point here is responsibility. As long as you call the library reshadow and claim that it does the job for you better than Shadow DOM could, an implicitly negative message that someone could read between the lines immediately appears.


Please don't treat this as an offense. Actually, I think that even mentioning Shadow DOM in more or less neutral to negative way might be for good.

Feel free to close the issue if that's not a proper place for such a discussion.

"as" property conflict

as property must be ignored when it set in a class / function components.

Error (TypeError: Cannot read property 'startsWith' of undefined):

<MenuItem {...menu} as={ThemeSettings} />

example

Fine:

<MenuItem {...menu} {...{ as: ThemeSettings }} />

reshadow beta roadmap

The roadmap:

  • finalize the API (almost done)
  • complete the docs
    • describe the usage with different frameworks and libraries
      • React
      • Preact
      • Vue
      • Svelte
      • htm
      • styled-components API
    • describe the runtime usage and usage with plugins:
      • reshadow/babel
      • reshadow/macro
      • reshadow/postcss
      • reshadow/webpack
      • reshadow/prettier
      • reshadow/eslint
    • describe the common use cases
    • the transition from styled-components
  • provide more examples and tutorials
  • provide some benchmarks

ParseError: Identifier is expected

When using @reshadow/postcss there is an error parsing this code:

<style reshadow>
  h1 {
    color: var(--color);
  }

  container {
    display: flex;
    flex-direction: column;
    padding: 10px;
    align-items: center;

    & button + button {
      margin-top: 10px;
    }
  }

  button {
    padding: 5px 10px;
    border-radius: 5px;
    font-size: 16px;
  }

  button[|size="s"] {
    font-size: 16px;
  }
</style>

<container>
  <button :size="s">Small</button>
  <button :size="m">Medium</button>
</container>

Error:

[!] (svelte plugin) ParseError: Identifier is expected
src/components/Button.svelte
10:     align-items: center;
11: 
12:     & .__button + .__button {
        ^
13:       margin-top: 10px;
14:     }
ParseError: Identifier is expected

Extending with variables bug

There are two components:

Base component (PrimaryButton) got correct styles:

{
    --c8_0: 2px;
    --c8_1: #4ba82e;
}

.___button_1tczc_1 {
    ...
    border-radius: var(--c8_0);
    background-color: var(--c8_1);
}

But extended component (WhiteButton) got incorrect styles:

{
    --c8_0: #000000;
    --c8_1: #ffffff;
}

.___button_17okg_1 {
    border: solid 1px var(--c8_0);
    background-color: var(--c8_1);
}

.___button_1tczc_1 {
    ...
    border-radius: var(--c8_0);  // bug here, should be 2px, but got #000000
    background-color: var(--c8_1);
}

styled function signature for typescript-styled-plugin compatibility

In reshadow
import styled from 'reshadow'
styled is a template literal , but typescript-styled-plugin this is not true. In typescript-styled-plugin styled.a, styled.div etc is template literal.

To solve this problem, you can add to default export any property same as current styled.
In this case, it will be possible to use as follows
image
Works hightlight, prettier formatting, linting

artifact appears as an element before

Hello,
(0.0.1-alpha.60)
again I’m talking about bugs from my program, if after viewing Divider -> Divider, to choose to watch the button SVGButton -> SVGButton large, then the artifact appears as an element before
SVGButton
style

instruction for switching

Good day,
Is there a described instruction for switching an application from styled-component to reshadow?

animation

in this button:

const ButtonMain = ({ children, handlerClick, disable, className, ...props }) => (
  <Main onClick={disable ? null : handlerClick} disable={disable} {...props} className={className}>
    {children}
  </Main>
);

const ripple = keyframes`
  0% {
    transform: scale(0, 0);
    opacity: 1;
  }
  20% {
    transform: scale(25, 25);
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: scale(40, 40);
  }
`;

const isLarge = ({ large }) =>
  large &&
  css`
    height: 54px;
    font-size: 16px;
    padding: 0 28px;
  `;

const isSmall = ({ small }) =>
  small &&
  css`
    height: 32px;
    font-size: 13px;
    padding: 0 14px;
  `;

const isShadow = ({ disable, border, bottom }) =>
  !disable &&
  !border &&
  !bottom &&
  css`
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.12), 0 1px 5px 0 rgba(0, 0, 0, 0.2);
  `;

const isShadowHover = ({ disable, border, bottom }) =>
  !disable &&
  !border &&
  !bottom &&
  css`
    box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14), 0 1px 7px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -1px rgba(0, 0, 0, 0.2);
  `;

const bgColor = ({ danger, second, disable, color, border }) => {
  const clr = disable
    ? BG_COLOR.disable
    : second
    ? BG_COLOR.second
    : danger
    ? BG_COLOR.danger
    : color
    ? color
    : border
    ? BG_COLOR.border
    : BG_COLOR.primary;
  return css`
    background-color: ${clr};
  `;
};

const bgColorHover = ({ danger, second, disable, border }) => {
  const clr = danger ? BG_COLOR_HOVER.danger : second ? BG_COLOR_HOVER.second : BG_COLOR_HOVER.primary;
  return (
    !disable &&
    !border &&
    css`
      background-color: ${clr};
    `
  );
};

const borderColorHover = ({ danger, second, disable, border }) => {
  const clr = danger ? BG_COLOR_HOVER.danger : second ? BG_COLOR_HOVER.second : BG_COLOR_HOVER.primary;
  return (
    !disable &&
    border &&
    css`
      border-color: ${clr};
    `
  );
};

export const Main = styled.button`
  position: relative;
  overflow: hidden;
  border: ${({ border }) => (border ? `2px solid ${BG_COLOR.second}` : 'none')};
  box-sizing: border-box;
  border-radius: ${({ rounded }) => (rounded ? '25px' : '2px')};
  display: inline-block;
  height: 36px;
  padding: 0 16px;
  font-size: 14px;
  ${isLarge};
  ${isSmall};
  text-transform: uppercase;
  vertical-align: middle;
  text-decoration: none;
  color: ${({ border }) => (border ? 'inherit' : '#fff')};
  ${bgColor}
  text-align: center;
  letter-spacing: 0.5px;
  transition: background-color 0.3s;
  cursor: ${({ disable }) => !disable && 'pointer'};
  outline: none;
  ${isShadow};
  :hover {
    ${bgColorHover};
    ${isShadowHover};
    ${borderColorHover};
  }
  :after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 5px;
    height: 5px;
    background: rgba(255, 255, 255, 0.5);
    opacity: 0;
    border-radius: 100%;
    transform: scale(1, 1) translate(-50%);
    transform-origin: 50% 50%;
  }
  :focus:not(:active)::after {
    animation: ${({ disable, border }) => !disable && !border && ripple} 1s ease-out;
  }
`;

not work : hover (cursor: pointer) and animation after click. (but in SC all work)

see (in my library -> Buttton -> Primary)

Additional alternative syntax for modifiers

In short: currently the only documented way to have BEM-style modifiers which are not based on rendered attributes is use:foo in HTML and [use|foo] in CSS.

There is an undocumented way to do the same with just _prop in HTML and [_foo] in CSS.

I think that should be an officially supported feature, available as a shortcut/alternative way to use the non-rendered props for conditional/targeted styling.

Pros:

  1. Shorter syntax. Just one symbol instead of four in both HTML and CSS.
  2. More consistent syntax: it is just the same prop in both HTML and CSS. The existent namespaces syntax makes it easy to make typos by writing something like [use:foo] in CSS.
  3. Easier to find using search: by looking for _foo you would find the modifier both inside CSS and HTML, while when you'd have use:foo and [use|foo], looking for just foo would return more messy result, and you'd need to use regex/multiple searches to find the modifier everywhere.
  4. Props/attributes that start from underscore are supported everywhere, so they can be used for plain HTML & CSS when testing component code in, for example, CodePen.
  5. This syntax could be used in places where the use: namespace is already taken, for example, in Svelte.
  6. This syntax would be familiar to those who know BEM.
  7. This syntax could be also used not only for “modifiers“, but for “elements”: instead of <content as="p"> in HTML and content {} in CSS you could write <p __content> in HTML and [__content] in CSS, making it harder to use default <div>s and promoting the usage of semantic elements. This, of course, would be also entirely optional and just as one of the potential ways to use reshadow.

Cons:

  1. A need to support/document two syntaxes. This is slightly mitigated by underscore syntax being very easy to implement (basically, just omit any props starting from _ from rendering in HTML).
  2. Potential inconsistency in the code. It should be possible to create a linter option to allow/disallow certain ways of doing such modifiers, this would allow developers to remove any potential inconsistencies.
  3. There is a possibility that some other project could have its own meaning for props starting with underscore, but in plain HTML such attributes are not valid (so it would be ok to discard them from the built HTML), and if it would be used just for styling in reshadow, there shouldn't be any substantial problems caused by this. I also didn't encounter such props in other projects, and have used them by myself as a way to name visual-only props.

Overall, I find the pros to overwhelm the cons, and I think we could have both ways of doing modifiers at the same time. The underscore props are implemented easily (and work already) and having multiple ways of doing things can make it easier for people to adopt reshadow, as they could choose the way that is closer to them.

Selectors starting with colon can be handled incorrectly

Sandbox: https://codesandbox.io/s/reshadow-cra-g02zb?file=/src/index.js:820-835

There are two examples: with :global and :not

  :global(.foo) {
    background: red;

    section & {
      background: lime;
    }
  }

  Button {
    border-width: 100px;

    :not(whatever) & {
      border-width: 5px;
    }
  }

Both these cases doesn't work correctly, but if we'd add starsm then they work:

  *:global(.foo) {
    background: red;

    section & {
      background: lime;
    }
  }

  Button {
    border-width: 100px;

    *:not(whatever) & {
      border-width: 5px;
    }
  }

This could probably be reproduced with other selectors as well.

@reshadow/styled - `background-image` is not displayed

When I try to set the background through background-image with url(), it's not displayed:

import React from "react";
import styled from "@reshadow/styled";

import ExitImg from "./exit.svg";

const Exit = styled.button`
    width: 21px;
    height: 21px;

    background: url(${ExitImg}) no-repeat;
`;

Also, Chrome DevTools highlights background as an incorrect value:

background: url(var(--6v6tgc_1))

[svelte] preprocesser adds unnecessary classes

From:

<svelte:head>
  <title>Home | Website</title>
</svelte:head>

Into:

<svelte:head class={__styled__.styles["__svelte:head"]} style={__styles__.$$style}>
  <title class={__styled__.styles["__title"]}>Home | Website</title>
</svelte:head>

This is related to #97

The preprocesser should skip any tags that start with <svelte: /> such as:

  • <svelte:self>
  • <svelte:component>
  • <svelte:window>
  • <svelte:body>
  • <svelte:head>
  • <svelte:options>

Non of these tags can be styled.

Conflict with babel-plugin-styled-components

I try to use @reshadow/styled in CRA.

I forgot to exclude babel-plugin-styled-components.

I have error:
_reshadow_styled__WEBPACK_IMPORTED_MODULE_6___default(...)(...).withConfig is not a function

How to use reshadow with TypeScript?

Property 'container' does not exist on type 'JSX.IntrinsicElements'. TS2339

import React from 'react'
import styled, { css } from 'reshadow/macro'

const styles = css`
  container {
    border: 1px solid red;
  }
`

class ReshadowPage extends React.Component<any> {
  render() {
    return styled(styles)`
      container {
        border: 1px solid green;
      }
    `(<container>Dummy</container>)
  }
}

export default ReshadowPage

[svelte] Reshadow + Svelte SSR Styles

When reshadow processes styles, they no longer are extracted to a css file, instead are evaluated by JavaScript at runtime.

Ex runtime styles:

<script>
    import {__init__ as __reshadow__} from '@reshadow/svelte';
    import {beforeUpdate as __bu__, afterUpdate as __au__} from 'svelte';

    import __styled__, {
        __extract__,
        set,
        create,
        css,
        use,
        map,
        __css__,
    } from '@reshadow/core';
    let newColor = 'blue';
    let color = 'tomato';
    const __styled___c8 = create([
        (__css__(
            `.___button_uwcly_1 {
    padding: 5px 10px;
    border-radius: 5px;
    font-size: 16px;
}

.___button_uwcly_1.__use--type_uwcly_1.__use--type_primary_uwcly_1 {
    color: green;
}

.___button_uwcly_1.__use--size_uwcly_1.__use--size_s_uwcly_1 {
    font-size: 14px;
    color: var(--c8_0);
}

.___button_uwcly_1.__use--size_uwcly_1.__use--size_m_uwcly_1 {
    font-size: 16px;
    color: var(--c8_1);
}`,
            /*__css_end__*/
            '1868321304',
        ),
        {
            __button: `___button_uwcly_1`,
            '_use--type': `__use--type_uwcly_1`,
            '_use--type_primary': `__use--type_primary_uwcly_1`,
            '_use--size': `__use--size_uwcly_1`,
            '_use--size_s': `__use--size_s_uwcly_1`,
            '_use--size_m': `__use--size_m_uwcly_1`,
        }),
    ]);

    $: __styles__ = __styled__(
        (set([__styled___c8], `--c8_0:${color};--c8_1:${newColor};`),
        __extract__()),
    );

    const __getStyles__ = () => __styles__;
    __reshadow__(
        {beforeUpdate: __bu__, afterUpdate: __au__},
        __getStyles__,
        () => {
            __styled__ = __styled__;
            map = map;
        },
    );
</script>

It would be ideal to return static styles in a style tag so Svelte could export them into a CSS file, and Reshadow will process dynamic styles at runtime.

Ex static styles:

<script>
    import {__init__ as __reshadow__} from '@reshadow/svelte';
    import {beforeUpdate as __bu__, afterUpdate as __au__} from 'svelte';

    import __styled__, {
        __extract__,
        set,
        create,
        css,
        use,
        map,
        __css__,
    } from '@reshadow/core';
    let newColor = 'blue';
    let color = 'tomato';
    const __styled___c8 = create([
        {
            __button: `___button_uwcly_1`,
            '_use--type': `__use--type_uwcly_1`,
            '_use--type_primary': `__use--type_primary_uwcly_1`,
            '_use--size': `__use--size_uwcly_1`,
            '_use--size_s': `__use--size_s_uwcly_1`,
            '_use--size_m': `__use--size_m_uwcly_1`,
        },
    ]);

    $: __styles__ = __styled__(
        (set([__styled___c8], `--c8_0:${color};--c8_1:${newColor};`),
        __extract__()),
    );

    const __getStyles__ = () => __styles__;
    __reshadow__(
        {beforeUpdate: __bu__, afterUpdate: __au__},
        __getStyles__,
        () => {
            __styled__ = __styled__;
            map = map;
        },
    );
</script>

<style>
    .___button_uwcly_1 {
        padding: 5px 10px;
        border-radius: 5px;
        font-size: 16px;
    }

    .___button_uwcly_1.__use--type_uwcly_1.__use--type_primary_uwcly_1 {
        color: green;
    }

    .___button_uwcly_1.__use--size_uwcly_1.__use--size_s_uwcly_1 {
        font-size: 14px;
        color: var(--c8_0);
    }

    .___button_uwcly_1.__use--size_uwcly_1.__use--size_m_uwcly_1 {
        font-size: 16px;
        color: var(--c8_1);
    }
</style>

<button
    {...map(
        'button',
        use({
            size: 's',
        }),
        {
            $$style: __styles__.$$style,
        },
    )}
    class="___button_uwcly_1 __use--size_uwcly_1 __use--size_s_uwcly_1">
    Small
</button>
<button
    {...map(
        'button',
        use({
            size: 'm',
        }),
        {
            $$style: __styles__.$$style,
        },
    )}
    class="___button_uwcly_1 __use--size_uwcly_1 __use--size_m_uwcly_1">
    Medium
</button>
<button
    {...map(
        'button',
        use({
            type: 'primary',
        }),
        {
            $$style: __styles__.$$style,
        },
    )}
    class="___button_uwcly_1 __use--type_uwcly_1 __use--type_primary_uwcly_1">
    Primary
</button>

Typed reshadow classnames

Hi, it would be cool to have option to generate typings for reshadow css files using this module https://github.com/Quramy/typed-css-modules.
it will improve development experience and will reduce problem with missing classnames in you r code.
I don't have concrete proposal for that but the best case would be.

  1. typed classnames/components for css files
  2. typed classnames/components for inline css
  3. typed props resolved from css
    Although 2 and 3 seems to be quite hard to implement, 1 case should be low hanging fruit though.
    I'd add such option to babel plugin or as external tool, I'm not sure at this moment. I'd be happy to hear your comments on this

`clearStyles` function needed

At test environment getStyles should return result only for specific test. So clearStyles function needed:

beforeEach(() => {
  clearStyles();
});

Unable to customize css tagged template with string expressions

There is a goal to assign all length in css code by multiplication to fix value. For example, let us take a segment 12px as a common, then all applicable lengths are in range [ 12, 24, ... ]. There is a reason to use string expressions in the reshadow css tagged template.

Down here there is an example to customize the width of children that way. Webpack answers with the following:

CssSyntaxError: /Users/avdotion/sandbox/reshadow-issue/src/App.js: reshadow/macro: /Users/avdotion/sandbox/reshadow-issue/src/App.js:2:3: Unclosed block Learn more: https://www.npmjs.com/package/reshadow

import React from 'react';
import {css} from 'reshadow/macro';

import Child from './Child';

const CHILD_WIDTH = '150px';

const childStyles = css`
  |child {
    width: ${CHILD_WIDTH};
    background-color: red;
  }
`;

const App = () => (
  <Child styles={childStyles} />
);

export default App;
import React from 'react';
import styled, {use} from 'reshadow/macro';

const Child = ({styles}) => styled(styles)(
  <use.child>
    child
  </use.child>
);

export default Child;

Please help to figure out how to solve the task with the reshadow tools.

problem with flex style ?

in this code:

const Divider = ({ children, max, end, start, ...props }) => (
  <Wrap max={max} end={end} start={start} {...props}>
    {children}
  </Wrap>
);
Divider.propTypes = {
  /** width = 100% */
  max: PropTypes.bool,
  /** what render on divider (text etc) */
  children: PropTypes.any,
  /** children located in the end divider */
  end: PropTypes.bool,
  /** children located in the start divider */
  start: PropTypes.bool
};
const style = css`
  content: '';
  background-color: #e0e0e0;
  height: 1px;
  flex: 1;
`;
const Wrap = styled.h4`
  display: flex;
  margin: 0 auto;
  width: ${({ max }) => (max ? '100%' : '80%')};
  align-items: center;
  :after {
    ${style};
    flex: ${({ start }) => start && '20'};
  }
  :before {
    ${style};
    flex: ${({ end }) => end && '20'};
  }
`;
export default Divider;

flex: ${({ start }) => start && '20'}; or with end, not work.
return flex = 1.

replace for flex: ${({ start }) => start ? '20' : '0'}; not help.

Css property alternative api

I have idea how to improve adoption and DX and I think using css prop instead of styled function should improve that quite a lot.
Here are few examples

const App = () => (
  <use.container
    css={css`
      h1 {
        font-size: 4rem;
      }
      Button {
        border: 1px solid ${red};
      }
      container {
        ///
      }
    `}
  >
    <h1>hello</h1>
    <Button>world</Button>
  </use.container>
)
const styles = css`
      h1 {
        font-size: 4rem;
      }
      Button {
        border: 1px solid ${red};
      }
      container {
        ///
      }
    `
const App = () => (
  <use.container css={styles}>
    <h1>hello</h1>
    <Button>world</Button>
  </use.container>
)
const styles = css`
  // should we treat top css as root classname
   padding-top: 20px;
   span {
     padding-right: 20px;
   }
`
const Title = () => (
  <h1 css={styles}>
    world <span> </span>
  </h1r>
)

composition

const commonStyles = css`
    padding-top: 20px;
   span {
     padding-right: 20px;
   }
`
const fileStyles = css`
   // some styles
`
const Title = ({ styles  }) => (
  <h1 css={[commonStyles, fileStyles, styles]}>
    world <span> hello </span>
  </h1r>
)

without root element

const styles = css`
   span {
     padding-right: 20px;
   }
`
const Title = ({ styles  }) => (
  <Fragment css={styles}>
    world <span> hello </span> !
  </Fragment>
)

Happy to hear you comments on that.

in dynamic styles with value combing

in dynamic styles with value combing, 1 class is created, it was expected that when calculating the value, a separate class will be created for each value.

const isFirst = ({ active }) =>
  active === 0 &&
  css`
    transform: translateX(0);
  `;

const isTwo = ({ active }) =>
  active === 1 &&
  css`
    transform: translateX(-${({ width }) => width}px);
  `;

const isThree = ({ active }) =>
  active === 2 &&
  css`
    transform: translateX(-${({ width }) => 2 * width}px);
  `;

const isFour = ({ active }) =>
  active === 3 &&
  css`
    transform: translateX(-${({ width }) => 3 * width}px);
  `;

const Wrapper = styled.div`
  height: 400px;
  width: ${({ width }) => 4 * width}px;
  display: flex;
  flex-flow: row nowrap;
  transition: all 1s cubic-bezier(0.4, 1.21, 1, 0.99);
  ${isFirst};
  ${isTwo};
  ${isThree};
  ${isFour};
`;

combing

[svelte] Runtime Error -> Uncaught ReferenceError: __styles__ is not defined

There is a warning:

(!) svelte plugin: $: has no effect outside of the top-level
src/components/Modal/Modal.svelte
46: 
47: onMount(() => {
48:   $: __styles__ = loadAsset(["https://js.stripe.com/v3"], "stripe", {
      ^
49:     numRetries: 3
50:   });

There seems to be an error somewhere, __styles__ is assigned to the wrong function.

Here is the component code:

<script>
  import { onMount } from "svelte";
  import loadAsset from "loadjs";
  import styled from "reshadow";

  import { connect } from "../../model";

  import Header from "./Header.svelte";
  import Footer from "./Footer.svelte";
  import LoginForm from "../LoginForm.svelte";
  import OrderList from "../OrderList.svelte";
  import Cart from "../Cart.svelte";
  import Checkout from "../Checkout.svelte";

  export let stripeKey;

  //! Uncaught ReferenceError: Cannot access 'open' before initialization
  let open, route, stripe;

  const [dispatch, modal] = connect("modal");

  $: open = $modal.open;
  $: route = $modal.route;

  $: currentRoute = route => {
    switch (route) {
      case "login":
        return LoginForm;

      case "orders": {
        return OrderList;
      }

      case "shipping":
      case "billing":
        return Checkout;

      case "cart":
      default:
        return Cart;
    }
  };

  function handleCloseEvent(event) {
    if (open) {
      dispatch("modal/closeModal");
    }
  }

  onMount(() => {
    loadAsset(["https://js.stripe.com/v3"], "stripe", {
      numRetries: 3
    });

    loadAsset.ready("stripe", {
      success: () => {
        stripe = Stripe(stripeKey);
      },
      error: () => {
        console.error("Moltin Btn - Stripe failed to load");
        return null;
      }
    });
  });

  styled`
    .shopkit-modal {
      transition: all 0.3s ease;
      background-color: #fff;
      position: fixed;
      top: 0;
      bottom: 0;
      right: 0;
      overflow-y: scroll;
      height: 100%;
      box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
      z-index: 1000000001;
      padding: 1.5rem;
      width: 100%;
      border-width: 0;
      max-width: 500px;
      opacity: ${open ? 1 : 0};
      visibility: ${open ? "visible" : "hidden"};
      transform: translateX(${open ? 0 : "525px"});
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      box-sizing: border-box;
      line-height: 1.15;
      -webkit-text-size-adjust: 100%;
      margin: 0;
      font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif;
      font-size: 15px;
    }

    .shopkit-modal-overlay {
      transition: all 0.3s ease;
      background-color: #333;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: 1000000000;
      opacity: ${open ? 0.3 : 0};
      visibility: ${open ? "visible" : "hidden"};
      overflow-x: ${open ? 100 : 0};
    }
  `;
</script>

<style>
  *,
  *::before,
  *::after {
    box-sizing: inherit;
    -webkit-font-smoothing: antialiased;
  }
</style>

<div class="shopkit-modal-overlay" on:click="{handleCloseEvent}" />

<div class="shopkit-modal">

  <Header {route} />

  <svelte:component this={currentRoute(route)} />

  <Footer />
</div>

Reshadow syntax proposal

I guess that separate style and jsx is more intuitively and understandable. I want to write some styles first, and create a component with JSX markup and some logic after that. Like this:

import React from "react";
import styled from "reshadow";

const css = styled`
    button {
      width: 250px;
    }
    span[use|size='l'] {
       text-transform: uppercase;  
    }
`;

export const CounterButton = ({children, size, ...props}) => {
  const [count, setCount] = useState(0); 
  return css(<button {...props} onClick={() => {setCount(count + 1)}}>
      <span use:size={size}>{children} {count}</span>
  </button>);
}

When I watch to constructions like this

export const CommonButton = ({ children, ...props }) =>
  styled`
    button {
      width: 250px;
    }
   span[use|size='l'] {
       text-transform: uppercase;  
    }
`(<button {...props}>
       {children}
  </button>);

I am experiencing cognitive stress and don't understand, what's going on. 😀

Splitting styles with props and makeup into two layers

Hi again! :)

I have another issue:

In an ideal way, I wanna have 2 layers of some view component: makeup and styles. Styles can use props from component: theme, state props, etc. I analyzed use cases in spec. files and create some demo component:

import styled from 'reshadow';
import * as React from 'react';
import { ThemeContext } from '../../../../theme/theme.context';

// It should be in separate file
const getStyles = ({ bgColor }) => css`
    button {
        width: 100px;
        height: 20px;
        background-color: ${bgColor};
        
        & > title {
            font-size: 16px;
        }
        
        & > icon {
            font-size: 20px;
        }
    }
`;

export class Button extends React.PureComponent {

    render() {
        return styled(getStyles({ bgColor: '#fc0' }))(
            <button as='div'>
                <title as='div'>{this.props.children}</title>
                <icon/>
            </button>
        );
    }
}

So, with importing css from reshadow/react compiler doesn't crash, but in the runtime there is no css-variable:
image

If I try with importing css from reshadow directly I get:

ERROR in ./src/sample-app/common/card/button/button.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
CssSyntaxError: /Users/dsitdikov/Projects/reshadow-research/src/sample-app/common/card/button/button.js:2:5: Unclosed block

May be an alternative way exists?

Create global style

Thanks for the great library!

I haven't found a solution for creating global theming in the application based on theme properties: for instance, the name of font families. (Something like this: https://www.styled-components.com/docs/api#createglobalstyle)

Does a solution with generating own globals.css from interpolated string by webpack plugin is ok? Or reshadow has something inside for resolving this problem?

Error with webpack-virtual-modules

After update dependencies, got next error:
Error: ENOENT: no such file or directory, open '/.cache/reshadow/1141933177_1.css'

Found problem in webpack/loader.

/.cache/reshadow/".concat(hash, ".css")

if use path.join(__dirname, "/cache/reshadow/".concat(hash, ".css")) all will work.

[svelte] SyntaxError: Unexpected token

When using <script context="module"> in a Svelte application there is an error:

[!] (svelte plugin) SyntaxError: Unexpected token (4:1)
src/App.svelte (4:1)
SyntaxError: Unexpected token (4:1)
    at Object.raise (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:6344:17)
    at Object.unexpected (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:7659:16)
    at Object.jsxParseIdentifier (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:3377:12)
    at Object.jsxParseNamespacedName (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:3387:23)
    at Object.jsxParseElementName (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:3398:21)
    at Object.jsxParseOpeningElementAt (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:3480:22)
    at Object.jsxParseElementAt (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:3513:33)
    at Object.jsxParseElement (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:3587:17)
    at Object.parseExprAtom (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:3594:19)
    at Object.parseExprSubscripts (/home/sokil/Desktop/Lab/reshadow/packages/svelte/node_modules/.registry.npmjs.org/@babel/parser/7.4.5/node_modules/@babel/parser/lib/index.js:8413:23)

1

Commented variable inside style

I have code with commented variable color:

  render() {
    const { b } = this.state
    // const color = b ? 'red' : 'green'
    return styled(styles)`
      container {
        /* background: ${color}; */
      }
    `(
      <container as="button" onClick={this.handleClick}>
        Dummy
      </container>,
    )
  }

And I have unexpected error:

./src/components/ReshadowPage/ReshadowPage.tsx
  Line 24:  'color' is not defined  no-undef

[svelte] preprocesser overwrites `this` attribute in an `<svelte:component />`

From: <svelte:component this={CountryFlag} {country}/>
Into: <svelte:component {...map("svelte:component", {...})} />

Svelte Compiler Error:

<svelte:component> must have a 'this' attribute
166:             {#each countries as country}
167:               <p class={__styled__.styles["__p"]}>
168:                 <svelte:component {...map("svelte:component", {
                     ^
169:             this: CountryFlag,
170:             country: country

[styled] handlers were lost

HI, with complex markup, event handlers were lost in the code:
(in FloatingAction )

class FloatingAction extends Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false
    };
  }

  togglerOpen = () => this.setState({ open: !this.state.open });
  openTrue = () => this.setState({ open: true });
  openFalse = () => this.setState({ open: false });

  render() {
    const { children, name, color, toggle, ...props } = this.props;
    const { open } = this.state;
    return (
      <Wrapper {...props} onMouseLeave={!toggle ? this.openFalse : null}>
        <MainFloat
          large
          color={color}
          {...props}
          onMouseEnter={!toggle ? this.openTrue : null}
          handlerClick={toggle ? this.togglerOpen : null}
        >
          <Icon name={name} />
        </MainFloat>
        <Wrap {...props}>
          <WrapAction {...props} open={open}>
            {children.map((el, indx) => (
              <WrapFloat key={indx} color={el.props.clr} small handlerClick={el.props.act}>
                {el}
              </WrapFloat>
            ))}
          </WrapAction>
        </Wrap>
      </Wrapper>
    );
  }
}
FloatingAction.propTypes = {
  children: PropTypes.any,
  toggle: PropTypes.bool,
  name: PropTypes.string,
  color: PropTypes.string
};
const isFixed = props =>
  props.fixed &&
  css`
    position: fixed;
    right: 30px;
    left: 30px;
    bottom: 23px;
    margin-left: 0;
    padding-top: 15px;
    margin-bottom: 0;
    text-align: end;
  `;
const Wrap = styled.div`
  position: relative;
  overflow: hidden;
  padding: 2px 0;
  position: absolute;
  overflow: hidden;
  z-index: 1;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
`;
const Wrapper = styled.div`
  position: relative;
  margin-left: 5px;
  ${isFixed};
  z-index: 997;
`;
const wrapIsFixed = props =>
  props.fixed &&
  css`
    flex-flow: row-reverse;
    top: 30px;
    right: 0;
    padding-right: 60px;
  `;
const openNotFix = props =>
  props.fixed
    ? css`
        transform: translateX(0);
      `
    : css`
        transform: translateX(50px);
      `;
const openFix = props =>
  props.fixed
    ? css`
        transform: translateX(100%);
      `
    : css`
        transform: translateX(-100%);
      `;
const WrapAction = styled.div`
  position: absolute;
  display: flex;
  top: 15px;
  left: 0;
  ${props => (props.open ? openNotFix : openFix)};
  transition: transform 0.2s linear;
  ${wrapIsFixed};
`;
const WrapFloat = styled(Float)`
  margin: 0 0 0 15px;
`;
const MainFloat = styled(Float)`
  left: ${props => (props.fixed ? '10px' : '-10px')};
  z-index: 9;
`;

export default FloatingAction;

Strange behavior on windows

On mac os, the behavior is expected. The same project on windows throws an next error:
ERROR in /.cache/reshadow/3267209689_1.css (CssSyntaxError)

For example:
Unknown word

1 | .___img_80kx0_1.__use--cover_80kx0_1 {\r
2 | -o-object-fit: cover;\r
3 | object-fit: cover\r
4 | }

Full error's stack trace:

ERROR in /.cache/reshadow/1610543195_1.css (/Users/rnskv/WebstormProjects/work4day/client/node_modules/css-loader/dist/cjs.js!/.cache/reshadow/1610543195_1.css)
Module build failed (from /Users/rnskv/WebstormProjects/work4day/client/node_modules/css-loader/dist/cjs.js):
CssSyntaxError

(2:5) Unknown word

1 | .___Button_59g3x_1 {\r

2 | margin-top: 12px;\r
| ^
3 | }\r

Dynamic theme

Hi! Thank you for your wonderful job! :)

I haven't found approaches for creating a dynamic theme as we've usually done it in Style Component. What do you think about this solution? Does another more correct way exist?

export function ThemeProvider({ theme, children }) {
    return React.createElement(ThemeContext.Provider, { children, value: theme });
};

export function withTheme(component) {
    return props => (
        <ThemeContext.Consumer>
            {theme => React.createElement(component, { ...props, theme })}
        </ThemeContext.Consumer>
    );
}

Each component I wrap by withTheme and get need properties inside it.

Allow implicit direct root styles

Right now we need to target each element explicitly.

const Button = ({children, styles, ...props}) => styled(styles)`
    button {
      border: 1px solid;
    }
`(
    <button {...props}>
        <content as="span">{children}</content>
    </button>
);

This makes it a bit cumbersome to restyle such component from outside (you need to mention the inner root component name explicitly):

<Button styles={css`button { background:red; }`}>
    click me
</Button>

My proposal: allow direct styles without a mention of the component name:

const Button = ({children, styles, ...props}) => styled(styles)`
  border: 1px solid;
`(
    <button {...props}>
        <content as="span">{children}</content>
    </button>
);

And then the “inline” styles become also much simpler:

<Button styles={css`background:red;`}>
    click me
</Button>

Other than reduced amount of code and more simple API for more simple components, this would be similar to how it is done in some other CSS-in-JS solutions like styled-components, making it easier for people to migrate.


Another similar feature that could be added: :root selector, which could be used to target the root element of our component. This would make it easier to use it as an explicit selector, but also without a need to know which element you need to target.


Some notes:

  1. Mixed declarations and rules: any declarations without a selector above them should be treated as wrapped with :root(), and rules alongside shouldn't be treated as nested inside the :root() unless they have a &.

  2. Both :root and implicit root styles should apply only to the one and only root component. So, in case there are some root styles, but then in HTML there is either a fragment or an array, those styles shouldn't be applied to anything. This would make the implementation easier and less confusing for such use-cases.

  3. While allowing multiple ways to do the same can be bad for consisting code, this can be enforced via a linting rules: allowing/disallowing direct root styles and/or :root ones.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.