integration/styles: better style scopingwhen using embedded dsfr
- the CSS that included the DSFR excerpt we needed styled some base html tags directly. It might collide with a service CSS already there. Now a postcss plugin applies a CSS prefix to everything - we output in the lib two DSFR file versions: one with prefixed selectors, one raw.
This commit is contained in:
10
packages/integration/package-lock.json
generated
10
packages/integration/package-lock.json
generated
@@ -20,6 +20,7 @@
|
||||
"import-single-ts": "^1.0.3",
|
||||
"patch-package": "^8.0.0",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-prefix-selector": "^1.16.1",
|
||||
"prettier": "^3.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@@ -3217,6 +3218,15 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-prefix-selector": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-prefix-selector/-/postcss-prefix-selector-1.16.1.tgz",
|
||||
"integrity": "sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"postcss": ">4 <9"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.16",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"import-single-ts": "^1.0.3",
|
||||
"patch-package": "^8.0.0",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-prefix-selector": "^1.16.1",
|
||||
"prettier": "^3.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import purgecss from "@fullhuman/postcss-purgecss"
|
||||
import prefixSelector from "postcss-prefix-selector"
|
||||
import autoprefixer from "autoprefixer"
|
||||
|
||||
export default {
|
||||
@@ -9,5 +10,20 @@ export default {
|
||||
variables: true,
|
||||
}),
|
||||
autoprefixer(),
|
||||
prefixSelector({
|
||||
prefix: ":where(.lasuite)",
|
||||
transform: function (prefix, selector, prefixedSelector, filePath, rule) {
|
||||
if (filePath.includes("dev.css") || filePath.includes("raw-dsfr.css")) {
|
||||
return selector
|
||||
}
|
||||
if (selector.includes(".lasuite") || selector === "html" || selector === ":root") {
|
||||
return selector
|
||||
}
|
||||
if (selector === "body") {
|
||||
return `.lasuite`
|
||||
}
|
||||
return prefixedSelector
|
||||
},
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
20
packages/integration/src/components/StylesFull.tsx
Normal file
20
packages/integration/src/components/StylesFull.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { ReactNode, useEffect } from "react"
|
||||
import fullStyles from "../styles/homepage-full.css?inline"
|
||||
import devStyles from "../styles/dev.css?inline"
|
||||
|
||||
export const StylesFull = ({ children }: { children: ReactNode }) => {
|
||||
useEffect(() => {
|
||||
if (document.querySelector("#styles-full")) {
|
||||
return
|
||||
}
|
||||
const style = document.createElement("style")
|
||||
style.id = "styles-full"
|
||||
style.innerHTML = fullStyles + devStyles
|
||||
document.head.appendChild(style)
|
||||
return () => {
|
||||
document.querySelector("#styles-full")?.remove()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return children
|
||||
}
|
||||
21
packages/integration/src/components/StylesStandalone.tsx
Normal file
21
packages/integration/src/components/StylesStandalone.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ReactNode, useEffect } from "react"
|
||||
import dsfrStyles from "../styles/raw-dsfr.css?inline"
|
||||
import homepageStyles from "../styles/homepage-gaufre.css?inline"
|
||||
import devStyles from "../styles/dev.css?inline"
|
||||
|
||||
export const StylesStandalone = ({ children }: { children: ReactNode }) => {
|
||||
useEffect(() => {
|
||||
if (document.querySelector("#styles-standalone")) {
|
||||
return
|
||||
}
|
||||
const style = document.createElement("style")
|
||||
style.id = "styles-standalone"
|
||||
style.innerHTML = dsfrStyles + homepageStyles + devStyles
|
||||
document.head.appendChild(style)
|
||||
return () => {
|
||||
document.querySelector("#styles-standalone")?.remove()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return children
|
||||
}
|
||||
@@ -8,14 +8,19 @@ import { Gaufre } from "./components/Gaufre"
|
||||
import { EmailOrProconnect } from "./components/Homepage/EmailOrProconnect"
|
||||
import { Email } from "./components/Homepage/Email"
|
||||
import { Proconnect } from "./components/Homepage/Proconnect"
|
||||
import { StylesFull } from "./components/StylesFull"
|
||||
import { StylesStandalone } from "./components/StylesStandalone"
|
||||
import services from "../../../website/src/data/services.json"
|
||||
|
||||
import "./styles/dsfr.css"
|
||||
import "./styles/homepage.css"
|
||||
import "./styles/gaufre.css"
|
||||
import "./styles/dev.css"
|
||||
|
||||
const serviceHomepage = ({ id, content }: { content?: ReactNode; id: string }) => {
|
||||
const serviceHomepage = ({
|
||||
id,
|
||||
content,
|
||||
styles = "standalone",
|
||||
}: {
|
||||
content?: ReactNode
|
||||
id: string
|
||||
styles: "standalone" | "full"
|
||||
}) => {
|
||||
const service = services.find(({ id: itemId }) => itemId === id)
|
||||
if (!service) {
|
||||
console.log(`Service ${id} not found, exiting`)
|
||||
@@ -23,29 +28,33 @@ const serviceHomepage = ({ id, content }: { content?: ReactNode; id: string }) =
|
||||
}
|
||||
const { name, tagline } = service
|
||||
|
||||
const StylesProvider = styles === "standalone" ? StylesStandalone : StylesFull
|
||||
|
||||
return {
|
||||
path: `/homepage-template-${id}`,
|
||||
label: `Homepage ${name}`,
|
||||
component: (
|
||||
<Homepage
|
||||
lasuiteApiUrl={import.meta.env.VITE_LASUITE_API_URL}
|
||||
entity="Gouvernement"
|
||||
tagline={tagline}
|
||||
serviceName={name}
|
||||
serviceId={id}
|
||||
logo={`/logos/${id}.svg`}
|
||||
homepageUrl="/"
|
||||
footerOptions={{
|
||||
description: "Un service de la Direction interministérielle du numérique",
|
||||
sitemapUrl: "/sitemap",
|
||||
a11yUrl: "/accessibilite",
|
||||
a11yLevel: "non compliant",
|
||||
termsUrl: "/mentions-legales",
|
||||
privacyUrl: "/donnees-personnelles",
|
||||
}}
|
||||
>
|
||||
{content || <Proconnect url="#" />}
|
||||
</Homepage>
|
||||
<StylesProvider>
|
||||
<Homepage
|
||||
lasuiteApiUrl={import.meta.env.VITE_LASUITE_API_URL}
|
||||
entity="Gouvernement"
|
||||
tagline={tagline}
|
||||
serviceName={`${name} - styles ${styles}`}
|
||||
serviceId={id}
|
||||
logo={`/logos/${id}.svg`}
|
||||
homepageUrl="/"
|
||||
footerOptions={{
|
||||
description: "Un service de la Direction interministérielle du numérique",
|
||||
sitemapUrl: "/sitemap",
|
||||
a11yUrl: "/accessibilite",
|
||||
a11yLevel: "non compliant",
|
||||
termsUrl: "/mentions-legales",
|
||||
privacyUrl: "/donnees-personnelles",
|
||||
}}
|
||||
>
|
||||
{content || <Proconnect url="#" />}
|
||||
</Homepage>
|
||||
</StylesProvider>
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -54,14 +63,17 @@ const routes = [
|
||||
serviceHomepage({
|
||||
id: "resana",
|
||||
content: <EmailOrProconnect proconnectUrl="#" />,
|
||||
styles: "full",
|
||||
}),
|
||||
serviceHomepage({
|
||||
id: "messagerie",
|
||||
content: <Email />,
|
||||
styles: "full",
|
||||
}),
|
||||
serviceHomepage({
|
||||
id: "tchap",
|
||||
content: <Email />,
|
||||
styles: "standalone",
|
||||
}),
|
||||
serviceHomepage({
|
||||
id: "france-transfert",
|
||||
@@ -75,9 +87,11 @@ const routes = [
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
styles: "standalone",
|
||||
}),
|
||||
serviceHomepage({
|
||||
id: "equipes",
|
||||
styles: "standalone",
|
||||
}),
|
||||
{
|
||||
path: "/gaufre",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
@import "./required-dsfr.css";
|
||||
@import "./prefixed-dsfr.css";
|
||||
@import "./homepage.css";
|
||||
@import "./gaufre.css";
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
--lasuite-primary: #000091;
|
||||
}
|
||||
|
||||
.lasuite {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (min-width: 78em) {
|
||||
.lasuite-container,
|
||||
.fr-container.lasuite-container {
|
||||
|
||||
2
packages/integration/src/styles/raw-dsfr.css
Normal file
2
packages/integration/src/styles/raw-dsfr.css
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "./fonts.css";
|
||||
@import "./dsfr.css";
|
||||
@@ -15,7 +15,8 @@ export default defineConfig({
|
||||
emptyOutDir: false,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
"css-required-dsfr": resolve(__dirname, "src/styles/required-dsfr.css"),
|
||||
"css-prefixed-dsfr": resolve(__dirname, "src/styles/prefixed-dsfr.css"),
|
||||
"css-raw-dsfr": resolve(__dirname, "src/styles/raw-dsfr.css"),
|
||||
"css-homepage": resolve(__dirname, "src/styles/homepage.css"),
|
||||
"css-gaufre": resolve(__dirname, "src/styles/gaufre.css"),
|
||||
"css-homepage-full": resolve(__dirname, "src/styles/homepage-full.css"),
|
||||
|
||||
@@ -64,13 +64,21 @@ vous l'utilisiez déjà ou non dans votre projet, un fichier CSS différent est
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Si vous voulez finement gérer vos styles car vous utilisez le bouton Gaufre sur tout votre site, vous pouvez choisir parmi tous les fichiers CSS présents :
|
||||
Si vous voulez finement gérer vos styles car vous utilisez le bouton Gaufre sur tout votre site,
|
||||
vous pouvez choisir parmi tous les fichiers CSS présents :
|
||||
|
||||
- `css/homepage-full.css` : contient l'extrait de DSFR nécessaire + les styles de la homepage + les styles de la gaufre,
|
||||
- `css/homepage-gaufre.css` : contient les styles de la homepage + les styles de la gaufre (sans DSFR),
|
||||
- `css/homepage-full.css` : contient l'extrait de DSFR nécessaire + les styles de la homepage + les
|
||||
styles de la gaufre,
|
||||
- `css/homepage-gaufre.css` : contient les styles de la homepage + les styles de la gaufre (sans
|
||||
DSFR),
|
||||
- `css/homepage.css` : contient les styles de la homepage (sans DSFR ni gaufre),
|
||||
- `css/gaufre.css` : contient les styles de la gaufre (sans DSFR ni homepage),
|
||||
- `css/required-dsfr.css` : contient l'extrait de DSFR nécessaire (rien d'autre),
|
||||
- `css/prefixed-dsfr.css` : contient l'extrait de DSFR nécessaire (rien d'autre),
|
||||
|
||||
Note : l'extrait de DSFR est toujours transformé pour que tous les sélecteurs soient derrière un
|
||||
sélecteur `:where(.lasuite)`. Ceci permet d'intégrer le DSFR sans risque de conflit avec d'autres
|
||||
styles, et sans impacter la spécifité du sélecteur. Si vous préférez utiliser un extrait de DSFR non
|
||||
transformé, utilisez `css/raw-dsfr.css`.
|
||||
|
||||
### 2. JS
|
||||
|
||||
@@ -124,10 +132,10 @@ savoir plus sur chaque composant.
|
||||
|
||||
##### Traduction
|
||||
|
||||
Si vous utilisez les composants React et que vous avez besoin de traduire votre page d'accueil,
|
||||
vous pouvez envelopper les composants avec le provider `LaSuiteTranslationsProvider` provenant du
|
||||
paquet, et lui passer en props vos `translations`. Les traductions françaises sont exportées en
|
||||
tant que `frTranslations`.
|
||||
Si vous utilisez les composants React et que vous avez besoin de traduire votre page d'accueil, vous
|
||||
pouvez envelopper les composants avec le provider `LaSuiteTranslationsProvider` provenant du paquet,
|
||||
et lui passer en props vos `translations`. Les traductions françaises sont exportées en tant que
|
||||
`frTranslations`.
|
||||
|
||||
#### Sans React
|
||||
|
||||
|
||||
Reference in New Issue
Block a user