简体   繁体   中英

Is it possible to define global styles with fixed css class names (or selectors) using merge-styles library?

I'm using Microsoft's Fluent UI library to build a React application. For styling of my own components I switched to using their merge-styles library, mainly because it integrates nicely with Fluent UI theming and all of their components. (I'm still not a big fan of CSS-in-JS, mainly because hot reloading in plain CSS / SCSS is smoother, but that's another story).

I still have a lot of components using SCSS, that I want to refactor over time to also use merge-styles. This is no problem for my own components, but it is for third-party components, that are solely stylable via specific CSS classes (plain JavaScript components, wrapped by a thin React component). So in order to style these using merge-styles I would have to use something like mergeStyles , but with the possbility to force completely fixed CSS class names. I know it's possible to somehow define the shape of the class name , but not providing a completely fixed name, where no number for uniqueness is appended. Did someone ever try this and found a way to achieve this using merge-styles alone or writing a small helper for this case?

Finally came up with a somewhat hacky version myself, temporarily monkey patching Fluent UI's Stylesheet.getClassName :

export function mergeStyleSetsWithFixedNames<TStyleSet1>(
  styleSet1: TStyleSet1 | false | null | undefined
): void;
export function mergeStyleSetsWithFixedNames<TStyleSet1, TStyleSet2>(
  styleSet1: TStyleSet1 | false | null | undefined,
  styleSet2: TStyleSet2 | false | null | undefined
): void;
export function mergeStyleSetsWithFixedNames<TStyleSet1, TStyleSet2, TStyleSet3>(
  styleSet1: TStyleSet1 | false | null | undefined,
  styleSet2: TStyleSet2 | false | null | undefined,
  styleSet3: TStyleSet3 | false | null | undefined
): void;
export function mergeStyleSetsWithFixedNames<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4>(
  styleSet1: TStyleSet1 | false | null | undefined,
  styleSet2: TStyleSet2 | false | null | undefined,
  styleSet3: TStyleSet3 | false | null | undefined,
  styleSet4: TStyleSet4 | false | null | undefined
): void;
export function mergeStyleSetsWithFixedNames(
  ...styleSets: Array<IStyleSet | undefined | false | null>
): void {
  const originalGetClassName = Stylesheet.getInstance().getClassName;
  Stylesheet.getInstance().getClassName = (displayName: string): string => {
    if (!displayName) {
      throw new Error('displayName is not optional when making a fixed style registration');
    }
    // Taken from https://stackoverflow.com/a/449000/773339
    if (!/^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/.test(displayName)) {
      throw new Error(`displayName ${displayName} has invalid characters`);
    }
    return displayName;
  };

  try {
    mergeStyleSets(...styleSets);
  } finally {
    Stylesheet.getInstance().getClassName = originalGetClassName;
  }
}

Example usage:

mergeStyleSetsWithFixedNames(
  { myClassName: { background: 'red' } },
  {
    myClassName: {
      background: 'green',
      ':hover': { background: 'yellow' },
    },
  }
);

which will define these CSS rules in the document:

.myClassName { background: green; }
.myClassName:hover { background: yellow; }

I know that this is not really a clean solution, but it's working for me and I just wanted to share it with everyone. I'm still open to better solutions:-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM