0

I have a fairly complex React App (v18) using FluentUI (v8) and building with Vite (v5). There are a collection of CSS variables which are influenced by the theme, but for my app they seem to be largely undefined. I do have a ThemeProvider defined. I'll try to include the relevant code.

const App = () => {
  ...

  const theme = createTheme({
    palette: {
      themePrimary: '#ff462d',
      themeLighterAlt: '#fff8f7',
      themeLighter: '#ffe1de',
      themeLight: '#ffc8c0',
      themeTertiary: '#ff9082',
      themeSecondary: '#ff5c47',
      themeDarkAlt: '#e63f29',
      themeDark: '#c23523',
      themeDarker: '#8f271a',
      neutralLighterAlt: '#faf9f8',
      neutralLighter: '#f3f2f1',
      neutralLight: '#edebe9',
      neutralQuaternaryAlt: '#e1dfdd',
      neutralQuaternary: '#d0d0d0',
      neutralTertiaryAlt: '#c8c6c4',
      neutralTertiary: '#a19f9d',
      neutralSecondary: '#605e5c',
      neutralPrimaryAlt: '#3b3a39',
      neutralPrimary: '#323130',
      neutralDark: '#201f1e',
      black: '#000000',
      white: '#ffffff',
    },
    components: {
      IconButton: {
        styles: {
          rootDisabled: {
            opacity: 0.5
          },
          root: {
            opacity: 1,
            backgroundColor: '#fff'
          }
        }
      }
    }
  });

  ...

  return (
    <FluentProvider theme={webLightTheme}>
      <ThemeProvider theme={theme}>
        <RouterProvider router={router} />
      </ThemeProvider>
    </FluentProvider>
  );
};

And then I have a Callout with some Checkbox components inside of it:

    const filterDialog = (
        <Callout
            className={styles.filterDialog}
            role="dialog"
            target={`#${filterButtonId}`}
            onDismiss={hideFilters}
        >
            <Text as="h1" block variant="large">Filters</Text>
            <Stack horizontal horizontalAlign="space-between" tokens={{ childrenGap: '5px' }}>
                <Checkbox size="large" label={ filterCheckboxLabel("Feedback") } />
                <Checkbox disabled={!filterByFeedback} label={ filterCheckboxLabel("Liked?") } />
                <Checkbox disabled={!filterByFeedback} label={ filterCheckboxLabel("Disiked?") } />
            </Stack>
        </Callout>
    );
    
    return (
        <Stack className={styles.top}>
        ...
                        <IconButton id={filterButtonId} disabled={false} onClick={toggleFilters} iconProps={{ iconName: "filter" }} />
                        {showFilterDialog ? filterDialog : null}
                        <IconButton disabled={true} iconProps={{ iconName: "trash" }} />
                    </StackItem>
                </Stack>
            </StackItem>
            ...

The checkboxes didn't render correctly (the spacing was off and the check box border was too thick, so I looked at the dev console and saw this:

screenshot of missing CSS vars

I'm at a bit of a loss as to how to diagnose what the issue might be... I'm no expert on the inner workings of FluentUI, hopefully someone here is!

UPDATE: I can see that the FluentProvider is setting those variables... but for some reason the checkbox styles aren't able to access them?

enter image description here

UPDATE: I've made some progress here... it looks like surfaces such as Callout are rendered in a separate hierarchy from the one the FluentProvider is wrapping called the the "Default Layer Host" context. So the question has become "How do I wrap the default layer host context with the FluentProvider?"

2

1 Answer 1

2

The issue was caused by the Callout being rendered in a separate hierarchy. By creating a custom LayerHost component and pointing the callout at that using the layerProps.hostId property, I was able to render it under the ThemeProvider:

...
  return (
    <FluentProvider theme={teamsLightTheme}>
      <ThemeProvider theme={theme}>
        <RouterProvider router={router} />
        <LayerHost id="custom-layer-host" style={{ position: 'fixed', top: 0, left: 0, width: '100%' }}  />
      </ThemeProvider>
    </FluentProvider>
  );
...

At first the callout was horizontally flattened, but adding those styles seems to have fixed the issue.

Here's what the Callout looks like now:

...
    const filterDialog = (
        <Callout
            className={styles.filterDialog}
            role="dialog"
            target={`#${filterButtonId}`}
            onDismiss={hideFilters}
            layerProps={{ hostId: "custom-layer-host", }}
        >
            <Text as="h1" block variant="large">Filters</Text>
            <Stack horizontal horizontalAlign="space-between" tokens={{ childrenGap: '5px' }}>
                <Checkbox checked={filterByFeedback} size="large" label={ filterCheckboxLabel("Feedback") } onChange={(_ev, data) => { data.checked ? setFilterByFeedback() : clearFilterByFeedback() }} />
                <Checkbox checked={filterByFeedbackPos} disabled={!filterByFeedback} label={ filterCheckboxLabel("Liked?") } onChange={(_ev, data) => { data.checked ? setFilterByFeedbackPos() : clearFilterByFeedbackPos() }} />
                <Checkbox checked={filterByFeedbackNeg} disabled={!filterByFeedback} label={ filterCheckboxLabel("Disliked?") } onChange={(_ev, data) => { data.checked ? setFilterByFeedbackNeg() : clearFilterByFeedbackNeg() }} />
            </Stack>
        </Callout>
    );
...
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.