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

Isolate global styles #339

Open
6 tasks done
xaddict opened this issue Oct 24, 2022 · 15 comments
Open
6 tasks done

Isolate global styles #339

xaddict opened this issue Oct 24, 2022 · 15 comments
Assignees
Labels
bug Something isn't working

Comments

@xaddict
Copy link

xaddict commented Oct 24, 2022

Describe the bug

When using a histoire.setup.js or style block inside a Vue 3 SFC to import global styles, these styles bleed out into the Histoire styling, overriding color, inputs and theming.

I don't think my application styling should inform Histoire's styling, right?

I suggest to either:

  • support the [scoped] attribute on style blocks inside SFCs
  • to make the whole Histoire render area an iframe no matter the settings for grid/single
  • to support a key styles in histoire.config.js

Reproduction

  • Create an Histoire Vue 3 project
  • Create a style sheet with *{color: black}
  • Import the style sheet in histoire.setup.js
  • Turn on dark mode
  • See how the style sheet overrides Histoire's dark theme.

https://stackblitz.com/edit/histoire-vue3-starter-cbmzxo?file=src%2Findex.css

System Info

System:
    OS: macOS 14.3.1
    CPU: Apple M2
    Memory: 24.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 21.6.2 - ~/.nvm/versions/node/v21.6.2/bin/node
    Yarn: 1.22.19 - ~/.yarn/bin/yarn
    npm: 10.5.0 - ~/.nvm/versions/node/v21.6.2/bin/npm
  Browsers:
    Brave Browser: 102.1.39.111
    Chrome: 106.0.5249.119
    Edge: 106.0.1370.52
    Firefox: 106.0.1
    Firefox Developer Edition: 125.0
    Firefox Nightly: 107.0a1
    Safari: 16.0
    Safari Technology Preview: 16.4
  npmPackages:
    @histoire/plugin-vue: ^0.17.15 => 0.17.15 
    @vitejs/plugin-vue: ^5.0.4 => 5.0.4
    histoire: ^0.17.15 => 0.17.15 
    vite: ^5.2.8 => 5.2.8

Used Package Manager

  • npm
  • yarn

Validations

@xaddict xaddict added the to triage This issue needs to be triaged label Oct 24, 2022
@stackblitz
Copy link

stackblitz bot commented Oct 24, 2022

Fix this issue in StackBlitz Codeflow Start a new pull request in StackBlitz Codeflow.

@tillsanders
Copy link

I have the same issue: #66 (comment)

I would love to see this be addressed in some way. I'm open to sponsoring a little to help make this happen :)

@troyere
Copy link

troyere commented Feb 10, 2023

Not strictly related, but there's no iframe at all in grid mode.

@Akryum
Copy link
Member

Akryum commented Feb 10, 2023

support the [scoped] attribute on style blocks inside SFCs

This is already supported :)

@xaddict
Copy link
Author

xaddict commented Feb 10, 2023 via email

@Akryum
Copy link
Member

Akryum commented Feb 10, 2023

Yeah it's a tough issue with no obvious solution (iframe comes with a lot of tradeoffs)

@tillsanders
Copy link

Would it be possible to run the stories inside a shadow DOM? That would isolate the styles completely.

@xaddict
Copy link
Author

xaddict commented Feb 14, 2023

I don't think so, as I have a global stylesheet that gets imported through the main javascript file. I can't import the stylesheet in every story.

@PhotonBursted
Copy link

After playing around a bit I noticed some things that may help this along. Perhaps sharing them sparks some new inspiration to get this resolved.

The styles imported in histoire.setup.js are included in the main Histoire app, as well as in the sandbox. It looks like they are "lazily loaded"; the styles from the sandbox only start "bleeding" once the first story is opened. The sandbox itself is an iframe which contains a rendering of the app with the styles included separately.

This can be confirmed by opening the Stackblitz in the original post, opening the app in a new tab, and playing around with it there. It starts out with three <style> tags in the app's <head>, which turns into four once a story is opened. After manually deleting the last one the Histoire app is perfectly displayed, while the styles in the sandbox are retained.

I guess this means the feature is somehow halfway there already? It definitely seems to be possible, at least.

@qexk
Copy link

qexk commented Apr 25, 2023

This is an issue that prevents histoire from being used in a ton of projects, unfortunately. I hope this’ll be solved soon.

Thanks to @PhotonBursted’s comment, I wrote this in my histoire.setup.ts file and it seems stable enough:

const isIframe = window.self !== window.top;
document.head
    .querySelectorAll("style[type='text/css']:not([data-vite-dev-id*='histoire'])")
    .forEach((style) => isIframe || style.remove());

It’s dirty but it works with v0.15.9

@reslear
Copy link
Contributor

reslear commented May 23, 2023

@Aksamyt but work only in dev mode

for using const isIframe = window.self !== window.top; good idea:

// src/histoire.setup.ts
import { defineSetupVue3 } from '@histoire/plugin-vue'
import { IonicVue } from '@ionic/vue'

export const setupVue3 = defineSetupVue3(async ({ app, story, variant }) => {
  // https://github.com/histoire-dev/histoire/issues/339#issuecomment-1522329599
  const isIframe = window.self !== window.top

  if (isIframe) {
    await import('@/theme/styles')
    app.use(IonicVue)
  }
})
// src/theme/styles.ts
import '@ionic/vue/css/core.css'
import '@ionic/vue/css/normalize.css'
import '@ionic/vue/css/structure.css'
import '@ionic/vue/css/typography.css'
import '@ionic/vue/css/padding.css'
import '@ionic/vue/css/float-elements.css'
import '@ionic/vue/css/text-alignment.css'
import '@ionic/vue/css/text-transformation.css'
import '@ionic/vue/css/flex-utils.css'
import '@ionic/vue/css/display.css'

import './variables.css'

And additional styles bundled in @ionic/vue package.

BUT

Screenshot 2023-05-23 at 17 37 59

Dynamic CSS load in one bundle:

  1. Not separated is used:
build: {
  rollupOptions: {
    output: {
      manualChunks: {
        ionic: ['@ionic/vue', '@ionic/vue-router'],
      },
    },
  },
},

proof:

// Force chunk strategy
config.build.rollupOptions.output = {

  1. cssCodeSplit: false proof:

cssCodeSplit: false,

  1. is patching package:
pnpm patch histoire@0.16.1

and remove code in 1 step, and set true in 2 step.

--- a/dist/node/build.js
+++ b/dist/node/build.js
@@ -114,29 +114,19 @@ export async function build(ctx) {
         config(config) {
             // Don't externalize
             config.build.rollupOptions.external = [];
-            // Force chunk strategy
-            config.build.rollupOptions.output = {
-                manualChunks(id) {
-                    if (!id.includes('@histoire/app') && id.includes('node_modules')) {
-                        for (const test of ctx.config.build?.excludeFromVendorsChunk ?? []) {
-                            if ((typeof test === 'string' && id.includes(test)) || (test instanceof RegExp && test.test(id))) {
-                                // Excluded from vendor chunk
-                                return;
-                            }
-                        }
-                        return 'vendor';
-                    }
-                },
-            };
+
             // Force vite build options
             Object.assign(config.build, {
                 outDir: ctx.config.outDir,
                 emptyOutDir: true,
-                cssCodeSplit: false,
+                cssCodeSplit: true,
                 minify: false,
                 // Don't build in SSR mode
                 ssr: false,
             });

We are getting the expected effect. But then the HTML generation breaks down, and this is the basic logic

need @Akryum help, because now using ionic is not fully production ready.

@noclat
Copy link

noclat commented Aug 26, 2023

Hi, any updates about isolating Histoire UI and global story styles? That issue defeats Histoire's purpose (sandboxing).

@Akryum Akryum self-assigned this Sep 24, 2023
@Akryum Akryum added bug Something isn't working and removed to triage This issue needs to be triaged labels Sep 24, 2023
@skstuder
Copy link

Was so excited at how fast we got this working, but now I am embarrassed to show anyone how bad it looks, because the global styles leak and make Histoire look terrible.

@SteinRobert
Copy link

SteinRobert commented Feb 27, 2024

const isIframe = window.self !== window.top;
document.head
    .querySelectorAll("style[type='text/css']:not([data-vite-dev-id*='histoire'])")
    .forEach((style) => isIframe || style.remove());

Also hacky - however works for now - took this and added it to setupCode:

export default defineConfig({
  plugins: [
    HstVue(),
    HstNuxt(),
  ],
  setupCode[`const isIframe = window.self !== window.top;
        const sandbox = window.location.pathname === '/__sandbox.html';
  document.head
      .querySelectorAll("style[type='text/css']:not([data-vite-dev-id*='histoire'])")
      .forEach((style) => (!isIframe && !sandbox) && style.remove());`]
})

This does not work with variants, since these don't use an iFrame.

@kuttyhub
Copy link

Hi, any updates about this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests