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",
|
"import-single-ts": "^1.0.3",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
|
"postcss-prefix-selector": "^1.16.1",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@@ -3217,6 +3218,15 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"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": {
|
"node_modules/postcss-selector-parser": {
|
||||||
"version": "6.0.16",
|
"version": "6.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
|
"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",
|
"import-single-ts": "^1.0.3",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
|
"postcss-prefix-selector": "^1.16.1",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import purgecss from "@fullhuman/postcss-purgecss"
|
import purgecss from "@fullhuman/postcss-purgecss"
|
||||||
|
import prefixSelector from "postcss-prefix-selector"
|
||||||
import autoprefixer from "autoprefixer"
|
import autoprefixer from "autoprefixer"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -9,5 +10,20 @@ export default {
|
|||||||
variables: true,
|
variables: true,
|
||||||
}),
|
}),
|
||||||
autoprefixer(),
|
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 { EmailOrProconnect } from "./components/Homepage/EmailOrProconnect"
|
||||||
import { Email } from "./components/Homepage/Email"
|
import { Email } from "./components/Homepage/Email"
|
||||||
import { Proconnect } from "./components/Homepage/Proconnect"
|
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 services from "../../../website/src/data/services.json"
|
||||||
|
|
||||||
import "./styles/dsfr.css"
|
const serviceHomepage = ({
|
||||||
import "./styles/homepage.css"
|
id,
|
||||||
import "./styles/gaufre.css"
|
content,
|
||||||
import "./styles/dev.css"
|
styles = "standalone",
|
||||||
|
}: {
|
||||||
const serviceHomepage = ({ id, content }: { content?: ReactNode; id: string }) => {
|
content?: ReactNode
|
||||||
|
id: string
|
||||||
|
styles: "standalone" | "full"
|
||||||
|
}) => {
|
||||||
const service = services.find(({ id: itemId }) => itemId === id)
|
const service = services.find(({ id: itemId }) => itemId === id)
|
||||||
if (!service) {
|
if (!service) {
|
||||||
console.log(`Service ${id} not found, exiting`)
|
console.log(`Service ${id} not found, exiting`)
|
||||||
@@ -23,29 +28,33 @@ const serviceHomepage = ({ id, content }: { content?: ReactNode; id: string }) =
|
|||||||
}
|
}
|
||||||
const { name, tagline } = service
|
const { name, tagline } = service
|
||||||
|
|
||||||
|
const StylesProvider = styles === "standalone" ? StylesStandalone : StylesFull
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: `/homepage-template-${id}`,
|
path: `/homepage-template-${id}`,
|
||||||
label: `Homepage ${name}`,
|
label: `Homepage ${name}`,
|
||||||
component: (
|
component: (
|
||||||
<Homepage
|
<StylesProvider>
|
||||||
lasuiteApiUrl={import.meta.env.VITE_LASUITE_API_URL}
|
<Homepage
|
||||||
entity="Gouvernement"
|
lasuiteApiUrl={import.meta.env.VITE_LASUITE_API_URL}
|
||||||
tagline={tagline}
|
entity="Gouvernement"
|
||||||
serviceName={name}
|
tagline={tagline}
|
||||||
serviceId={id}
|
serviceName={`${name} - styles ${styles}`}
|
||||||
logo={`/logos/${id}.svg`}
|
serviceId={id}
|
||||||
homepageUrl="/"
|
logo={`/logos/${id}.svg`}
|
||||||
footerOptions={{
|
homepageUrl="/"
|
||||||
description: "Un service de la Direction interministérielle du numérique",
|
footerOptions={{
|
||||||
sitemapUrl: "/sitemap",
|
description: "Un service de la Direction interministérielle du numérique",
|
||||||
a11yUrl: "/accessibilite",
|
sitemapUrl: "/sitemap",
|
||||||
a11yLevel: "non compliant",
|
a11yUrl: "/accessibilite",
|
||||||
termsUrl: "/mentions-legales",
|
a11yLevel: "non compliant",
|
||||||
privacyUrl: "/donnees-personnelles",
|
termsUrl: "/mentions-legales",
|
||||||
}}
|
privacyUrl: "/donnees-personnelles",
|
||||||
>
|
}}
|
||||||
{content || <Proconnect url="#" />}
|
>
|
||||||
</Homepage>
|
{content || <Proconnect url="#" />}
|
||||||
|
</Homepage>
|
||||||
|
</StylesProvider>
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,14 +63,17 @@ const routes = [
|
|||||||
serviceHomepage({
|
serviceHomepage({
|
||||||
id: "resana",
|
id: "resana",
|
||||||
content: <EmailOrProconnect proconnectUrl="#" />,
|
content: <EmailOrProconnect proconnectUrl="#" />,
|
||||||
|
styles: "full",
|
||||||
}),
|
}),
|
||||||
serviceHomepage({
|
serviceHomepage({
|
||||||
id: "messagerie",
|
id: "messagerie",
|
||||||
content: <Email />,
|
content: <Email />,
|
||||||
|
styles: "full",
|
||||||
}),
|
}),
|
||||||
serviceHomepage({
|
serviceHomepage({
|
||||||
id: "tchap",
|
id: "tchap",
|
||||||
content: <Email />,
|
content: <Email />,
|
||||||
|
styles: "standalone",
|
||||||
}),
|
}),
|
||||||
serviceHomepage({
|
serviceHomepage({
|
||||||
id: "france-transfert",
|
id: "france-transfert",
|
||||||
@@ -75,9 +87,11 @@ const routes = [
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
styles: "standalone",
|
||||||
}),
|
}),
|
||||||
serviceHomepage({
|
serviceHomepage({
|
||||||
id: "equipes",
|
id: "equipes",
|
||||||
|
styles: "standalone",
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
path: "/gaufre",
|
path: "/gaufre",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
@import "./required-dsfr.css";
|
@import "./prefixed-dsfr.css";
|
||||||
@import "./homepage.css";
|
@import "./homepage.css";
|
||||||
@import "./gaufre.css";
|
@import "./gaufre.css";
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
--lasuite-primary: #000091;
|
--lasuite-primary: #000091;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lasuite {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 78em) {
|
@media (min-width: 78em) {
|
||||||
.lasuite-container,
|
.lasuite-container,
|
||||||
.fr-container.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,
|
emptyOutDir: false,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: {
|
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-homepage": resolve(__dirname, "src/styles/homepage.css"),
|
||||||
"css-gaufre": resolve(__dirname, "src/styles/gaufre.css"),
|
"css-gaufre": resolve(__dirname, "src/styles/gaufre.css"),
|
||||||
"css-homepage-full": resolve(__dirname, "src/styles/homepage-full.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>
|
</TabItem>
|
||||||
</Tabs>
|
</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-full.css` : contient l'extrait de DSFR nécessaire + les styles de la homepage + les
|
||||||
- `css/homepage-gaufre.css` : contient les styles de la homepage + les styles de la gaufre (sans DSFR),
|
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/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/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
|
### 2. JS
|
||||||
|
|
||||||
@@ -124,10 +132,10 @@ savoir plus sur chaque composant.
|
|||||||
|
|
||||||
##### Traduction
|
##### Traduction
|
||||||
|
|
||||||
Si vous utilisez les composants React et que vous avez besoin de traduire votre page d'accueil,
|
Si vous utilisez les composants React et que vous avez besoin de traduire votre page d'accueil, vous
|
||||||
vous pouvez envelopper les composants avec le provider `LaSuiteTranslationsProvider` provenant du
|
pouvez envelopper les composants avec le provider `LaSuiteTranslationsProvider` provenant du paquet,
|
||||||
paquet, et lui passer en props vos `translations`. Les traductions françaises sont exportées en
|
et lui passer en props vos `translations`. Les traductions françaises sont exportées en tant que
|
||||||
tant que `frTranslations`.
|
`frTranslations`.
|
||||||
|
|
||||||
#### Sans React
|
#### Sans React
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user