Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[✨] a way to declare component$ which shares parent component styling scope #2726

Closed
mhevery opened this issue Jan 27, 2023 · 4 comments
Closed
Labels
STATUS-1: needs triage New issue which needs to be triaged TYPE: enhancement New feature or request

Comments

@mhevery
Copy link
Contributor

mhevery commented Jan 27, 2023

Is your feature request related to a problem?

export default component$(() =>{
  useScopedStyles$(`.red {color: red}`);
  return <div>
      <Link class="red">text</Link>
    </div>;
});

The above does not work because the red is scoped to the component. This makes styling Link component rather difficult.

The styling would work fine if the Link component was light-component, but because Link needs to use useMethods() it needs to be wrapped in component$()

Describe the solution you'd like

Have the ability to declare component$() in a way that inherits the scoped styling of the parent. This is useful:

  1. With <Link/>
  2. When refactoring large components into smaller components. It would be nice to be able to cleave off components without getting into trouble with styling.
export default component$(() =>{
  useScopedStyles$(`.red {color: red}`);
  return <div>
      <Child class="red">text</Child>
    </div>;
});


export const Child = component$((props) => {
  useScopedStyles$(`.center {...}`);
  return <span {...props} class="center">Hello: <Slot/></span>
}, {
  styleScopping: 'inherit'
})

The resulting DOM would be:

<style q:style="ABC">.red.⭐️ABC{color: red}</style>
<div class="⭐️ABC">
  <style q:style="XYZ">.center.⭐️XYZ{...}</style>
  <span class="red ⭐️ABC ⭐️XYZ">
    Hello: text
  </span>
</div>

Describe alternatives you've considered

Using lite-component, but that is not possible in all of the cases (such as when the component needs use-method access)

Additional context

Related issues which should be solved with this:

@mhevery mhevery added TYPE: enhancement New feature or request STATUS-1: needs triage New issue which needs to be triaged labels Jan 27, 2023
@heinthanth
Copy link

heinthanth commented Jan 27, 2023

@mhevery Can you check this? I made an initial PoC.

export default component$(() =>{
  useStylesScoped$(`.red {color: red}`);
  return <div>
      <Child>text</Child>
    </div>;
});

export const Child = component$((props) => {
  useStylesScoped$(`.center {...}`);
  return <span {...props} class="center red">Hello: <Slot/></span>
}, {
  styleScope: 'inherit'
})

will render a child with red text.

Currently, the option is passed with

jsx(
      Virtual,
      {
        ['q:cmpOption']: options,
        [OnRenderProp]: componentQrl,
        [QSlot]: props[QSlot],
        [_IMMUTABLE]: (props as any)[_IMMUTABLE],
        children: props.children,
        props,
      },
      finalKey
    ) as any;

and passed to Context at render-ssr.ts/renderNodeVirtual. I'm not sure if it's a good way to pass options to component.

@mhevery
Copy link
Contributor Author

mhevery commented Jan 27, 2023

I think @manucorporat is more familiar with that code.

@manucorporat
Copy link
Contributor

I think adding options to the component itself is the wrong approach, this is an issue only with useStyleScoped, i would rather give an option to it, like this:

export default component$(() =>{
  useStylesScoped$(`.red {color: red}`, { scope: 'descendants' });
  return <div>
      <Child>text</Child>
    </div>;
});

However... this is a problem created because useStyleScoped itself, it's not an problem inherited from Qwik, in fact there are scoping solution that does not have this problem:

  • CSS modules
  • Vanilla-extract
  • Any CSS-in-JS
  • Tailwind

I think it's a safe model to keep styles scoped to the component itself, if there is a need to pass it to a children right now it's possible to do it by doing:

export default component$(() =>{
  const {scopeId} = useStylesScoped$(`.red {color: red}`);
  return <div>
      <Child class={[scopeId, 'red'}}>text</Child>
    </div>;
});

We could make it even easier by doing:

export default component$(() =>{
  const {className} = useStylesScoped$(`.red {color: red}`);
  return <div>
      <Child class={scopedClass('red')}>text</Child>
    </div>;
});

#2730

@wmertens
Copy link
Member

Closing this as scopeId is currently the accepted way to make this work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
STATUS-1: needs triage New issue which needs to be triaged TYPE: enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants