mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.36k stars 32.13k forks source link

Provide a version of the components without any styles #6218

Open kof opened 7 years ago

kof commented 7 years ago

Material-UI as "material to build UI", not "material design"!

I am wondering if it would make any sense to extract the core components of material-ui in a separate, completely non-opinionated and naked library and make all material-ui specific design decisions a theme + stuff like Ripple.

As an outcome I would expect:

oliviertassinari commented 7 years ago

@kof Thanks for starting this discussion. You have opened quite some interesting issues lately. What do you have in mind with core components? Does this discussion take into account a unstyled version of the library?

kof commented 7 years ago

unstyled version of the library

Exactly, I called it naked ui, it should be just core stuff required for every theming/ux.

From my perspective of the next codebase, much of the modules that we could extract away from the lib have already been extracted. We don't try to reinvent the wheel.

I think a lot more is not really material-ui specific but generic and we can do more generic if we use variants mui/material-ui#6130

kof commented 7 years ago

For e.g. the components innside of internal/** look like mostly generic also I see more stuff can become generic.

kof commented 7 years ago

Just from what I have looked into: Form, Divider, Avatar, Icon, Input and probably lots of others can be also generic nacked components and mui just a theme + variants on top.

oliviertassinari commented 7 years ago

Thanks for clarifying your motivation. I see two ways to move forward into that direction.

  1. We decouple the styles from the components. Meaning that we would expose unstyled components that accept a classes property object with various customization class names points. Then, to expose the material version, we would have to wrap all those naked components with an Higher-order Component.
  2. If we move forward with mui/material-ui#6130, we could allow users to override all the rules using the theme. We could imagine adding an option to start from unstyled rules.
kof commented 7 years ago

In order to achieve naked components we would need to implement mui/material-ui#6130 first anyways. Also naked components would need some core styles, theme unrelated.

mbrookes commented 7 years ago

Reminds me of Reapp -the very first React component library I tried for the personal project that ultimately led to my involvement in Material-UI. :-) (In fact, I'm almost certain I discovered React from it, not the other way around!).

The components had no inherent styling (https://github.com/reapp/reapp-ui/tree/master/src/components, and a theme included the style objects for each component: https://github.com/reapp/reapp-ui/tree/master/src/themes/ios/styles

Only the iOS theme was developed, but the intention was that it could support Material Design, or other styles.

I think there was a certain naivety in that, since looking at the more complicated components in Material-UI such as the pickers (as malformed as they may be), some of the sub-component hierarchy and functionality is dictated by the design that the component is aiming to meet.

Making that generic could be tricky. (Not impossible, but certainly an added burden.). All that said, I'm certainly on-board with the idea in principal, but I'll leave it to those smarter than me to figure out the implementation details.

kof commented 7 years ago

Yeah, such complex components like Datepicker are hard to make generic and probably not a good idea too. They may be implemented within material-ui but using all the generics from the nacked library.

A nacked library can provide though all necessary parts for it, so that assembling own custom picker is not as hard any more.

jgoux commented 7 years ago

I like this. :+1:

In a perfect world we'd have a style-independent repository with core-components that we can use across multiple projects with theme adapters. (Like a material theme, a bootstrap theme...).

There is this repository : https://github.com/react-component/ which follow more or less the same idea, minus the styles that don't seem "open".

stunaz commented 7 years ago

If it is gonna take years to implement, I really don't think it is a good path to follow. I'd rather have material-ui focus only on material design component implementation with react.

Zuurkern commented 7 years ago

@stunaz I agree we should focus on porting more components to the next branch first and make them fully in line with Material Design guidelines. Also improve their APIs and generally make them more robust.

This is a great idea though and I'd love to see it implemented. Especially after reading mui/material-ui#6130

kof commented 7 years ago

This is about next steps, for sure we need to release "next" first.

esseswann commented 7 years ago

Please don't fall into illusion of abstraction levels. As @stunaz @Zuurkern @kof say not all components are good material for that

Janpot commented 6 years ago

In case it could serve as inspiration, I remember the angular team coming up with what they call "a CDK" for their material UI library. https://blog.angular.io/a-component-dev-kit-for-angular-9f06e3b4b3b4

oliviertassinari commented 4 years ago

📊 To the developers who have upvoted the issue:

We have started to explore a possible solution to the unstyled problem with this approach (you can play with the above on this codesansbox). See pull request mui/material-ui#22435

import * as React from 'react';
import styled from '@emotion/styled';
import { SliderBase } from '@material-ui/core/Slider';

const StyledSlider = styled(SliderBase)`
  height: 2px;
  width: 100%;
  padding: 13px 0;
  display: inline-block;
  position: relative;
  cursor: pointer;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;

  & .MuiSlider-rail {
    display: block;
    position: absolute;
// …

SliderBase is a version of the component with almost no dependencies (it's very bundle-size efficient). The component can be customize with a couple of approaches.

<SliderBase components={{ Root: StyledRoot, thumb: StyledThumb }} />
<SliderBase componentsProps={{ root: { className: 'mt-5' }, thumb: { style: { background: 'red' } } }} />
<SliderBase classes={{ root: 'my-root-class', thumb: 'my-thumb-class' }} />

What do you think about it? What solution would you like to see us deliver?

Alternative approaches

The main advantage of the above proposal is that it solves two or event three problems at the same time. 1. It solves the migration to emotion/styled-components, 2. it allows us to test the potential for unstyled-components, and 3. it's likely required to deliver a second theme, in addition to Material Design (love-hate relationship of the community around this design spec). There are two other directions that we can push in order to better address the "unstyled" use case.

Hooks

It's what we have done with the Combo Box and the Pagination components.

Pros:

Cons:

Closer to DOM nodes

Same proposal but with components closer to the DOM nodes. For instance, with a slider, we could have:

<Slider defaultValue={20}>
  <SliderTrack />
  <SliderRail />
  <SliderThumb />
</Slider>

Pros:

Cons:

Benchmark

yordis commented 4 years ago
<SliderBase components={{ Root: StyledRoot, thumb: StyledThumb }} />

The previous one allows you to do all the other ones, and incentivize component-based interfaces, without having to introduce APIs that subjectively are simple to use.

jgoux commented 4 years ago

The best of both worlds would be to construct the Base component out of the hooks IMO.

By default you can go with the Base which covers 90% of the usual cases, and you can "build your own Base" when things get fancy.

This is more or less the approach taken by Adobe's spectrum and I think it's a very effective one.

darasus commented 3 years ago

It might be a good chance to introduce theme-ui spec to the stack. Why not make a styling solution as dependency injection. One big theme spec specific provider on top + some style resolvers, depending which styling solution you prefer (seems like a good api design challenge).

oliviertassinari commented 3 years ago

You can play with the first unstyled component in https://next--material-ui.netlify.app/components/slider-styled/#unstyled-slider.


It might be a good chance to introduce theme-ui spec to the stack.

@daraselia The unstyled components have no knowledge of any specific style or theming convention. We have seen this very theme specification request come up a few times, for instance, we chat about it with @shinework. So far, we haven't seen a strong argument for changing the current theme specification that outweighs the opportunity cost. I have seen the following arguments so far:

Counter argument: we will revamp the system engine in v5. The value proposition for developers is well explained in the answer of https://github.com/jsxstyle/jsxstyle#why-write-styles-inline-with-jsxstyle. We need to push further. We used to have that in v0.x with inline styles.

Counter argument: so is Material-UI's theme structure. It's not strongly tightened to Material Design. Actually, we will bring a second theme mui/material-ui#22485 to make this better resonate.

Limitation: isn't Material-UI's theme structure more frequently used? What about the BC implications for current MUI users? Can a unification really make a difference for the overall industry? What about Tailwind that went with its own theme structure?

I don't think that theme-ui should be discussed more on this specific thread. If you want to make the case for it, please open a detailed RFC :).

yordis commented 3 years ago

I recently had to implement something related to this (outside Mui, just general feedback).

From Internal Code Perspective

import React from "react";
import CheckIcon from "@material-ui/icons/Check";

// It will allow people to get used to some nomenclature across the ecosystem,
// I would love to see some alignment in the across community honestly, maybe
// take the lead in the future about, and align with other libraries.

// I am not sure what type should I use for general React component type, so
// I used React.FC.
type PropsWithComponents<
  C extends Partial<{ [key: string]: React.FC }>,
  P
> = P & {
  components?: C;
};

type Components = Partial<{
  SuccessMessage: React.FC<{ message: string }>;
}>;

type MyInputProps = React.PropsWithChildren<
  PropsWithComponents<
    // We could pass a list of names instead of an interface, but then we lose
    // the flexibility of documenting some properties that may be pass to a
    // particular component. I think would be worth for the internal code
    // documentation
    Components,
    {
      success?: boolean;
      failure?: boolean;
    }
  >
>;

function DefaultSuccessMessage() {
  return (
    <CheckIcon className="text-green-500 absolute right-0 bottom-0 mb-4 mr-4" />
  );
}

function MyInput(props: MyInputProps) {
  // Pattern for look up the components, and adding the default implementation
  // if we have one.
  const SuccessMessage =
    props.components?.SuccessMessage ?? DefaultSuccessMessage;
  // Internal state
  const message = ""

  return (
    <div>
      ....
      {/* We pass internal states as displayed with message */}
      {/*
        We control the render of the component, this will allow Mui to decide
        lazy evaluation of when particular component needs to actually render.
        Classical issues when people expect components pre-rendered, they mess
        up the performance.
      */}
      {props.success ?? <SuccessMessage message={message} />}
    </div>
  );
}

From End User Code Perspective

Statically

import React from "react";
import CheckIcon from "@material-ui/icons/Check";

// Perfect, it is just a component, we define statically, everything is good
// The downside is that we dont have access to MyUI scope
function SuccessMessage() {
  return (
    <span className="absolute flex items-center font-semibold right-0 bottom-0 mb-4 mr-4 uppercase text-green-500">
      <CheckIcon className="mr-1" />
      Coupon applied
    </span>
  );
}

function MyUI() {
  return (
    <div>
      ...
      <MyInput
        success={true}
        components={{ SuccessMessage }}
      />
    </div>
  )
}

Accessing the scope

import React from "react";
import CheckIcon from "@material-ui/icons/Check";

function SuccessMessage() {
  return (
    <span className="absolute flex items-center font-semibold right-0 bottom-0 mb-4 mr-4 uppercase text-green-500">
      <CheckIcon className="mr-1" />
      Coupon applied
    </span>
  );
}

// Just the old factory function.
function createSuccessMessage(myUIProps: any) {
  // Perfect we have access to all the scopes
  return (muiProps)=> <SuccessMessage />
}

function MyUI(props: any) {
  const SuccessMessage = createSuccessMessage(props)
  return (
    <div>
      ...
      <MyInput
        success={true}
        components={{ SuccessMessage }}
      />
    </div>
  )
}

General thoughts

I think this approach is clean enough and easy to understand and maintain.

Worth pointing once again about the fact that Mui controls the rendering of the components, this has been the biggest issue using 3rd parties libraries and Juniors not knowing why the app is so slow in the List Page, where they are rendering an entire unused Modal per item because they eagle evaluation the rendering of those components in their component definition (please help me indirectly).

Concerns?

oliviertassinari commented 3 years ago

An update on the priorities. We plan to push this as soon as v5 has made enough progress. We will work on it hand in hand with mui/material-ui#22485 to have a healthy constraint.

michaldudak commented 3 years ago

Status update: I recently joined the core team and unstyled components are my main focus. As for the implementation I'm leaning towards having unstyled components using hooks. I published a draft of a PR with this approach and I'd love to know what you think about the proposed API.

You can follow our progress in mui/base-ui#10.

avgspacelover commented 2 years ago

I read this issue as a newbie today and i cannot understand how developers follow a specific feature for more than 5+ years as it seems really complex .is there any meaningful way for me to contribute to this feature ?

mbrookes commented 2 years ago

@antariksh17 The answer is linked in the comment above yours: https://github.com/mui/base-ui/issues/10