Vistaprint

Adding the Visage files

Adding the Visage files to your project

Configuring Visage

Domain Configuration

First, you'll need to configure your project to set which domain to use for the URLs for the various Visage assets.

To set the stage for this section, we'll start with a quick refresher on how the browser handles <link>/<script> tags. Feel free to skip the next paragraph if you're already familiar with optimizing load times by using same-domain requests!

Once your page's HTML is downloaded, the browser is going to discover a bunch of <link> and <script> tags that reference remote assets that need to be download. If those secondary assets are served from a different domain than your HTML, then the browser will need to go through an additional connection process for each new domain. However, if those assets are served from the same domain as your HTML, then the browser can use its existing connection which saves time!

By default, Visage will return URLs with the https://visage-design-system.prod.merch.vpsvc.com domain, but we've also made sure that our assets are available via each of our production domains (e.g. vistaprint.ie). So, when building for production, we recommend you should specify the appropriate domain via the domain option in VisageProvider's styleConfig.

e.g. if you're building a page that will be accessed via vistaprint.es, you should specify domain: "vistaprint.es".

Supported Domains

From v5.6.1 onwards, Visage provides a list of supported domains as a hash variable called VISAGE_SUPPORTED_DOMAINS. Developers can use this hash to pass the appropriate domain via the domain option of VisageProvider's styleConfig.

Special cases:

  • VISAGE_SUPPORTED_DOMAINS.default.cdn - (default): A generic domain that can be used when you don't know which domain the page will be served from, or when you don't care about the domain perf optimization. This is the right choice for local development.

  • VISAGE_SUPPORTED_DOMAINS.relative : This will get you back a relative URL that will try to find the Visage files on whatever domain the page happens to be served from. Using "relative" doesn't work for local builds or in staging environments, so we discourage it, except for temporary domains such as https://next.vistaprint.foo. A default prefix visage will be appended after the domain assuming your Visage files are hosted there.

Note:

  1. If you pass a domain that doesn't exist in our supported list, Visage will fallback to the default domain viz. https://visage-design-system.prod.merch.vpsvc.com

  2. In case you want to disable domain checks and fallback and want to adhere to whatever you are passing in the domain, you can set the option domainValidation under styleConfig to false

  3. While running locally, you may get warnings on your browser console stating `Using default domain in VisageProvider`. This is just to remind you that you are using the default domain and to switch to the same domain which you will use on production. These warnings will be suppressed in non-local environments.

jsx
import {
PackageType,
VisageProvider,
STYLE_KEYS,
VISAGE_SUPPORTED_DOMAINS,
} from '@vp/visage'
;<VisageProvider
styleConfig={{
domain: VISAGE_SUPPORTED_DOMAINS.vistaprint.ie, // Should be based on your locale
packageType: PackageType.LATEST, // Optional
fileType: FileType.HASHED, // Optional
defaultKeys: [STYLE_KEYS.some_key], // Optional
}}
>
<YourRootElement> ... </YourRootElement>
</VisageProvider>

Choosing Visage Canonical Domain

For using a Visage domain in your code that can handle local development, the next site, and the live site; we suggest two approaches as mentioned below:

LiveSite domain fetch approach: As the name suggests, firstly the live site data is fetched, and from that data, the canonical domain corresponding to your locale and tenant is determined. To check out a working example, you can refer to the Home Page repository whose link is shared below.

  1. Home page - Canonical domain resolution

  2. Home page - Visage domain assignment

VISAGE_SUPPORTED_DOMAINS approach: The first approach will work perfectly in the majority of cases; though it may cause unexpected behavior (CORS error) on the next site at times when the next site is being migrated to production. The reason for that is as follows:

As the migration process starts, the live site API returns the production domain for the locale. So, even when your next site's VisageProvider expects the next domain, it results in passing the actual production domain. Now the issue at this point is that the production domain may not have the visage proxy configured, which may get configured at some later point. So, during this time gap with the live site URL being updated and the visage proxy configuration, your page on the next site may seem to be broken.

To tackle the above problem, you can use the combination of tenant and locale to get the canonical domain from `VISAGE_SUPPORTED_DOMAINS` as shown in the sample code snippet below.

javascript
const nextSiteLocales = {
vistaprint: ['fr'],
promotique: ['nl', 'de'],
} // Add other next locales
function getDomain(tenant, locale, isProduction) {
if(isProduction && nextSiteLocales[tenant]?.includes(locale)) {
return VISAGE_SUPPORTED_DOMAINS.relative
}
return VISAGE_SUPPORTED_DOMAINS[tenant][locale]
}
// inside the VisageProvider
domain = {getDomain(String(tenant), country.toLowerCase(), isProduction)}

Notice how VISAGE_SUPPORTED_DOMAINS.relative is being used. Since the relative URL won't work on local builds or in staging environments, it is being enabled only on production. Also, you might be thinking that once the next site is migrated to live, you will need to update the hardcoded `nextSiteLocales`, but no need to worry as relative URL will work on live site as well; so you can plan to update it rather than hastily update it.

Eager Loading

In Visage with React, we lazy-load fonts and styles by default, based on the style configuration in <VisageProvider>. To make this work, the root element in React must be wrapped inside <VisageProvider> tags. (The VisageProvider props are optional, and are documented below.) To eagerly load all / some styles, you will need to pass the respective style keys (like you did in Visage 4), using defaultKeys key in the styleConfig prop, as shown below:

jsx
<VisageProvider
styleConfig={{
defaultKeys: [STYLE_KEYS.accordion, STYLE_KEYS.carousel],
/* This will eagerly load styles for accordion and carousel only. */
}}
>
...
</VisageProvider>

Note: As mentioned above, the component styles are lazy-loaded by default, and this should work great for pages that are server-side rendered. However, we recommend eagerly loading styles instead if your application is not doing server-side rendering; this will avoid a “flash of unstyled content” as the component mounts and the stylesheet loads.

Disable Loading

Visage will load all the styling by default even if the defaultKeys is not provided or initialized with an empty array. To disable this behavior, pass null as the styleConfig and in case the font still needs to be loaded, this can be achieved by passing the path to fonts in fontFolderPrefix as shown below:

<VisageProvider styleConfig={null} fontFolderPrefix={fontFolder}>...</VisageProvider>

VisageProvider Props

NameValuesDetails
globalLinkComponenttype: React.Node default: aUsed by Link component to figure out which component to use for rendering a link.
disableBrowserNameToDocumenttrue or false default: false Recommendation: Do not disable.Visage adds CSS classes to the html tag based on the user’s browser; some components use these classes for correct styling, You can disable this behavior by setting the value as true.
styleConfigDefault Value: {} Pass null or false to disable auto-style loading.Explained below.
fontsFolderPrefixDefault: Auto computed based on the style config. Set null if you want to disable auto-font load.You would only need to set this to a url if you have your own font hosting and do not want to use the fonts provided by Visage. You can disable the font loading by passing null. and then load the fonts yourself.

StyleConfiguration

NameValuesDetails
defaultKeysArray<STYLE_KEYS.component_name>List of styles to load eagerly. Core and utilities are always loaded; you don’t need to explicitly include here. Stylesheets not specified here will be automatically lazy-loaded; using defaultKeys will not disable lazy-loading other stylesheets.
fileTypeVERSIONED or HASHED Default: HashedIf not passed, the default fileType is HASHED. HASHED: When this option is passed, Visage will load the stylesheets with a file content hash in their filenames, instead of a version number in their file path. This enables a new version of Visage to reuse previously-cached files when a new version is released. This is recommended for everyone. Versioned: This option loads the files which have the version number in their file path. This is useful while debugging to verify if the correct version files are being loaded. This should be avoided in production.
domain{your domain name}The domain should be same as the final domain of your deployment locale. Keeping it same, will ensure better performance. All the locales are/domains are backed via CDN (mostly Akamai) eg: https://www.vistaprint.ie Default: Directly loaded from s3 bucket url. (https://visage-design-system.prod.merch.vpsvc.com/) Note: do not use the default in production. This is not hosted via CDN, so using it will decrease your site performance considerably. Also, we recommend using VISAGE_SUPPORTED_DOMAINS variable to provide a valid domain as shown in the first code snippet.
domainValidationboolean (default: true)Set to false in case you do not wish Visage to validate the domain provided in the styleConfig from a list of whitelisted domains. We don't recommend this as domain validation ensures you use canonical domain and is error free on production environment.
packageTypeLATEST or NEXT or EXPERIMENTAL or DEV. Default: PackageType.LATEST PackageType.<Enum_Values>You can choose the package type, to try a particular release. LATEST is the one to use for most production pages. NEXT represents a forthcoming version, typically used while it’s in development. EXPERIMENTAL is for experimental branches.
baseUrl{your baseUrl}This is auto-computed based on the domain and the urlPrefix provided. Do not provide this when using Visage on a Vistaprint or Promotique domain. You can override it if you need it, for the below use-cases: Your required domain doesn’t falls under vistaprint or promotique. eg. Cimpress, or 99designs, etc. You have deployed the static files by yourself. default: domain + urlPrefix
urlPrefix{your urlPrefix}The url-prefix that needs to be applied after the domain. Do not provide when using on Vistaprint or Promotique. This is required when you have your own mapping. When on Vistaprint/Promotique, use the default urlPrefix. You may need to provide this explicitly for the following use-cases: You have your own URL Mapping, which is different from /visage under Jetstream. You have your own deployment, possibly for other companies under Cimpress. eg. 99designs, etc. default: if s3 url: empty_string else: /visage

Fetching URLs Manually

Note that if you're using react components, these scripts are not necessary. These are only for folks who are using non-react visage-components.

Styles & Scripts

Visage exports two functions getStyleKeys and getScriptKeys to manually fetch the style and scripts URLs respectively. You may want to use these methods in the following cases:

  1. You are using the Visage in vanillaJS app, instead of React app.

  2. In React, if you are not using the VisageProvider component to wrap your root element, you might want to use getStyleKeys method to pass in the configuration for loading styleURLs and other configurations, per the above table.

Both the functions accept two parameters:

  1. keys: It can be either an array or a set of keys (STYLE_KEYS for getStyleKeys & SCRIPT_KEYS for getScriptKeys).

  2. config: A config object whose configuration type is the same as StyleConfiguration discussed in the VisageProvider props section above. If you don't want to pass any configuration, pass an empty object.

typescript
import { getStyleKeys, STYLE_KEYS } from '@vp/visage'
// Signature
// getStyleKeys(keys: STYLE_KEYS[], config: StyleConfiguration)
// Eg:
getStyleKeys([STYLE_KEYS.some_component], {
domain: 'https://www.vistaprint.ie',
})

Basically, you provide a list of components to getStyleKeys and it returns you a list of URLs that should be included on your page in order to style those components properly.

Note:

  • Visage will load the component's styling even if you don't provide the keys; however that will be lazily loaded and you may see a little jitter in the page. Providing the keys will eagerly load the component's styling.

  • Every component has its own style key. e.g. the styles for the Link component is included in the core CSS, so there is no STYLE_KEYS.link.

You should only include other style keys if you are actually using the component. This ensures that you don't end up downloading unnecessary CSS.

Similarly, getScriptKeys can be used along with SCRIPT_KEYS in order to include visage scripts on your page.

jsx
import { getScriptKeys, SCRIPT_KEYS, FileType } from '@vp/visage'
// or use `require` if the script is running server-side/build-time
//const { getScriptKeys, SCRIPT_KEYS } = require("@vp/visage");
const urls = getScriptKeys([SCRIPT_KEYS.core, SCRIPT_KEYS.modalDialog], {
fileType: FileType.VERSIONED,
})
console.log(urls)
// [
// "https://visage-design-system.prod.merch.vpsvc.com/latest/v5/5.14.1/vanilla-js/core.min.js"
// "https://visage-design-system.prod.merch.vpsvc.com/latest/v5/5.14.1/vanilla-js/modalDialog.min.js"
// ]

If your page is making use of Visage themes, in the same function getStyleKeys, you can use THEME_KEYS to get a list of URLs that should be included on your page in order to add the theme's style.

Fonts

Employ function getFontsPrefix to manually fetch the prefix URL for the fonts and use it to load the font you want. A sample has been shown below:

jsx
import { getFontsPrefix, FileType } from '@vp/visage'
const fontPrefix = getFontsPrefix({
domain: 'https://www.vistaprint.ie',
fileType: FileType.VERSIONED,
})
const fontUrls = [
`${fontPrefix}/Graphik-Bold-Web-v2.woff2`,
`${fontPrefix}/Graphik-Medium-Web-v2.woff2`,
`${fontPrefix}/Graphik-Regular-Web-v2.woff2`,
]
console.log(fontUrls)
// https://www.vistaprint.ie/visage/latest/v5/5.14.1/fonts/Graphik-Bold-Web-v2.woff2
// https://www.vistaprint.ie/visage/latest/v5/5.14.1/fonts/Graphik-Medium-Web-v2.woff2
// https://www.vistaprint.ie/visage/latest/v5/5.14.1/fonts/Graphik-Regular-Web-v2.woff2

Getting the static files

Use npm to get the CDN URLs for a specific version

visage provides JavaScript methods for getting the URLs for the static files needed for the components.

Use a specific version via the CDN instead of npm

This is preferred in the case of non-react applications.

For instance, if you want to use version 5.3.4 of the visage, your URL for the Standard Hero stylesheet will look like this: https://visage-design-system.prod.merch.vpsvc.com/latest/v5/5.3.4/library/components/standard-hero/standard-hero.min.css (Note the presence of the version number right in the URL.)

You can use these URLs on your page.

Please note: all your components on your page should use the same version of Visage. Mixing and matching versions on a per-component basis may create display errors!

Examples

Visit the below repository to find the basic implementation of Visage (multiple major versions) for the most commonly used frameworks and libraries:

https://gitlab.com/vistaprint-org/merchandising-technology/visage/visage-examples

Also consider below examples if loading styles manually via functions getStyleKeys, getScriptKeys.

React Helmet

React Helmet is a React-first way of modifying the document <head>.

Make sure that your setup is configured to server-side-render the Helmet. If SSR is not configured properly, the styles will load after the page's initial render and customers will see a flash of unstyled content. Server-side rendering the Helmet ensures that the link tags are present in the document at parse-time and the browser can properly block rendering until the styles are ready.

If you're using Gatsby, you can take advantage of gatsby-plugin-react-helmet which will configure SSR for you!

jsx
import { Helmet } from 'react-helmet'
import { getStyleKeys, STYLE_KEYS } from '@vp/visage'
export function VisageIncludes() {
return (
<Helmet>
{getStyleKeys([
STYLE_KEYS.core,
STYLE_KEYS.textButton,
STYLE_KEYS.tabs,
]).map(url => (
<link key={url} rel="stylesheet" type="text/css" href={url} />
))}
</Helmet>
)
}

HTML Webpack Plugin

If you're generating your HTML via the html-wepack-plugin, you can add the html-webpack-tags-plugin which is capable of injecting link tags into the generated HTML.

javascript
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin')
const { getStyleKeys, STYLE_KEYS } = require('@vp/visage')
module.exports = {
// other webpack stuff
// ...,
plugins: [
new HTMLWebpackPlugin(),
new HTMLWebpackTagsPlugin({
links: getStyleKeys([
STYLE_KEYS.core,
STYLE_KEYS.textButton,
STYLE_KEYS.tabs,
]),
}),
],
}

Create React App

In order to avoid flickering when you load your page client-side, styles must be present in the HTML served by your server/CDN. Usually, you would use html-webpack-tags-plugin in order to inject your links at build time, but CRA doesn't allow you to update webpack's configuration unless you eject your project.

One way to avoid ejecting the project is to use craco, a configuration manager for CRA.

To install craco you can follow the instructions here. To inject the links, you will also need to install html-webpack-tags-plugin.

When you have craco installed, you only need to create a craco.config.js file with the following content, using the style keys required by your project.

javascript
const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin')
const { getStyleKeys, STYLE_KEYS } = require('@vp/visage')
const requiredStyleKeys = [
STYLE_KEYS.grid,
STYLE_KEYS.accordion,
STYLE_KEYS.modalDialog,
]
module.exports = {
webpack: {
plugins: {
add: [
[
new HtmlWebpackTagsPlugin({
links: getStyleKeys(requiredStyleKeys),
usePublicPath: false,
}),
'append',
],
],
},
},
}

Other setups

If you're not using react-helmet or html-wepback-plugin, your may need to manually generate the link tags at build time. This will vary from setup to setup; feel free to reach out to in #help-visage on Slack if you need support.

Here is a custom setup example using a HandleBars template (contributed by Ashley Ryan):

Convert your index.html file into a Handlebars template and add a section like this one for the Visage styles (and another section scripts if necessary)

handlebars
{{! The required Visage CSS will be injected below at build time }}
{{#each styles}}
<link rel="stylesheet" type="text/css" href="{{this}}">
{{/each}}

and compile that template into your final index.html file at build-time with a node script:

javascript
const { getStyleKeys, STYLE_KEYS } = require('@vp/visage')
const Handlebars = require('handlebars')
const fs = require('fs')
const templatePath = './templates/index.html.hbs'
const styleURLs = getStyleKeys([STYLE_KEYS.core, STYLE_KEYS.textButton])
const makeIndexHTML = Handlebars.compile(
fs.readFileSync(templatePath, { encoding: 'utf8' }),
)
fs.writeFileSync('./public/index.html', makeIndexHTML({ styles: styleURLs }), {
encoding: 'utf8',
})

Which version of Visage should you use?

We would encourage you to use the most current version unless you have a pressing need to do otherwise. See the Versioning page for more.