As more and more orgs are moving to Lightning Experience, a question has persisted with regard to their Visualforce pages — what do we do about the styling of the pages? In this post, we’ll take a look at a new feature for styling Visualforce pages, currently in Beta in Winter ’18.

A little history

Shortly after the initial release of Lightning Experience, the Salesforce Lightning Design System (SLDS) team made it possible to download the CSS framework and include it as a static resource for use in Visualforce pages. With the static resource in place and being referenced in the Visualforce page, a developer could simply add the appropriate SLDS style to an Apex tag, for example, <apex:commandButton action="{!URLFOR($Action.Lead.New)}" value="New" styleClass=”slds-button slds-button_neutral”/> and have the button take on the appropriate Lightning Experience styling.

The ability to add SLDS via a static resource added a challenge however, because Salesforce releases a new version 3 times a year, and as a part of each release, there is a new version of SLDS. This meant, to stay in sync, that developers needed to download a new version of SLDS and update their static resource for each release.

But with the introduction of the <apex:slds /> tag in Spring ’17, Visualforce pages gained access to the always-up-to-date version of SLDS, eliminating the need for a static resource version. In order to ensure that styles coming from SLDS didn’t collide with custom CSS in the page, the SLDS team also introduced the concept of scoping when using SLDS in a Visualforce page. The necessary scoping class .slds-scope is added automatically to the page unless you are using the flag applyBodyTag="false" on the apex:page tag, in which case, areas of the page using SLDS styles need to be wrapped in a <div> with a class of slds-scope applied.

Reaching for nirvana

But even with the easy access to SLDS, a couple of implementation challenges still remained. The first challenge was the fact that some Visualforce tags don’t allow the use of a styleClass attribute and therefore there’s no way to say “I want to use this SLDS class on this Visualforce element”. An example of this is <apex:sectionHeader>. Behind the scenes, there is a very simple reason for this omission — this tag creates numerous HTML elements within the page. So the question would be, which HTML element do you want the class or classes applied to?

To overcome this challenge, developers needed to go to the SLDS site and grab the markup for a corresponding page element and replace the Apex tag with the SLDS markup. In the sectionHeader example, the page header component from SLDS would be the appropriate replacement.

Which then brings us to the second challenge. We’ve added the SLDS stylesheet to the page, applied styles to page elements, and in some cases we have even replaced page elements with SLDS markup. And our users back on Classic are freaking out! Because they’ve been seeing all of these changes as well.

A better approach is to leave the pages with their standard Classic look in Classic, and only deliver the new styling to users in Lightning Experience. And luckily this is easy to do by detecting the environment of the user. Here’s a simple Apex method which returns a boolean of true if the user is using Theme3 — which is Classic.

Armed with the knowledge of the user’s environment, we can conditionally include the SLDS stylesheet with the rendered attribute. <apex:slds rendered="{! !isClassic}"/>  only includes the SLDS stylesheet when the user is not in Classic. So even though a Visualforce element has a SLDS style applied, like the apex:commandButton example above, if the stylesheet is not included in the page it will still display like a button in Classic.

Not only can you conditionally include the SLDS stylesheet, by adding the rendered attribute to an <apex:outputPanel> you can conditionally include/exclude entire page regions. For example, to handle the sectionHeader issue, you could use:

Using the method above, SLDS markup is being rendered for Lightning Experience users, but the apex:sectionHeader is being rendered for Classic users.

And now for a little bit of Winter magic

Now you’ve seen how, with a little work, you can style a Visualforce page to look like Lightning Experience and deliver it only to our Lightning Experience users. But with Winter ’18 it gets even easier.

In the Winter ’18 release, there is a new attribute on the apex:page tag named lightningStylesheets. Simply set its value to true, and voila, your Visualforce page looks like Lightning Experience in Lightning Experience, and still looks like Classic in Classic!

Instead of this:

Visualforce rendered as Classic in Lightning Experience

<apex:page lightningStylesheets=“true”> gives us this:

Visualforce rendered as Lightning Experience

I told you, it’s magic! Okay, it’s not really magic, it’s just some magical styling. The SLDS team has worked with the Visualforce team to create a Lightning Experience version of the standard stylesheets used by Visualforce. This means that the standard CSS classes from Classic are still being used on the HTML elements generated by Visualforce, but the style definitions have been changed.

As cool as this is, be aware, it also means “your mileage may vary”. In other words, your Visualforce pages will take on a Lightning Experience look, but may not be an exact match with a native Lightning Experience page. Certain elements, like the apex:sectionHeader will look similar to a page header in Lightning Experience, but not exact. As mentioned, this feature is simply restyling the markup generated by the Visualforce page. Because the DOM, or HTML structure, for pages and page elements is different between Classic and Lightning Experience, the C in CSS, the cascade, doesn’t necessarily match — and therefore, an exact match with the look of Lightning Experience can not be guaranteed.

Mind the gap

This feature is currently in Beta, meaning that it can and will change as the team gets reports back from the real world on bugs and issues that need to be addressed.

With that in mind, there are a couple of important things to know as you begin to implement this feature in your org. First, developers or admins need to manually add the lightningStylesheets attribute to their Visualforce pages in Winter ’18. Because Visualforce tags can be combined in various ways, you need to look at how these style changes affect each page.

Second, the lightningStylesheets="true" flag is not adding the SLDS stylesheet to the page. It is delivering a modified version of the standard stylesheets used by Visualforce. This means that if you have previously added standardStylesheets="false" to your page in order to suppress the default stylesheets from Visualforce, you will not get the new styling because that styling is coming from the standard stylesheets. In order to get the Lightning Experience styling, you will need to remove the standardStylesheets attribute from the page, set it to standardStylesheets="true", or if necessary, contextually set its value to true if the user is in Lightning Experience.

Finally, if you do want to use classes from SLDS to style non-Visualforce tags, or maybe to use utility classes for additional spacing, modifying alignment, etc., you will still need to add the current version of SLDS using the apex:slds tag as mentioned above.

In conclusion

We are really excited about this new capability in Winter ’18, and hope you are too! The visual appearance of a page is important. With the initial releases of the Lightning Experience, a lot of work around updating Visualforce was left to individual teams and developers. Allowing the platform to take over the styling of Visualforce pages in Lightning Experience is going to be a huge time-saver for developers and admins alike. Let us know what you think on the Success Community for Lightning Now or tag us on social media with the hashtag #LEX4VF! To learn more about styling Visualforce pages with SLDS, check out the Build Flexible Apps with Visualforce Pages and Lightning Components project on Trailhead.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS