[SalesForce] Guidance on styling existing Visualforce pages adequately in Lightning Experience without major rework

PPS

This thread pre-dates the <apex:page lightningStylesheets="true" ...that is the first thing to try now.

Original Thread

Note the existing point – these pages have already been written (there are a lot of them) and our aim is to make them continue to work in Classic and look OK in Lightning Experience. Eventually they will be replaced by native Lightning, so I'm looking for a pragmatic "good enough" solution for now. It's the styling part I'm interested in here not other aspects of compatibility. (Also see this similar question.)

The output of this Add a Custom Scope to the CSS in your Static Resource has two CSS files:

  • salesforce-lightning-design-system-ltng.css
  • salesforce-lightning-design-system-vf.css

with a small percentage of the CSS different.

Using the vf one this page:

<apex:page standardController="Contact" recordSetVar="cs">
<div class="slds cv">
    <apex:stylesheet value="{!URLFOR($Resource.CVSLDS, 'assets/styles/salesforce-lightning-design-system-vf.css')}"/>
    <apex:form>
        <apex:sectionHeader title="{!$ObjectType.Contact.labelPlural}" subtitle="All""/>
        <apex:pageMessages />
        <apex:pageBlock title="{!$ObjectType.Contact.labelPlural}">
            <apex:pageBlockTable value="{!cs}" var="c">
                <apex:column value="{!c.FirstName}"/>
                <apex:column value="{!c.LastName}"/>
                <apex:column value="{!c.Birthdate}"/>
                <apex:column value="{!c.CreatedDate}"/>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</div>
</apex:page>

produces the appropriate fonts but in line with this SLDS Visualforce platform page's mention that apex:pageBlock is not supported quite a lot of Visualforce default styling remains e.g. panel background colors.

Is there a recipe for the minimum addition of styleClass values that result in more complete but not necessarily perfect SLDS styling? Or has any one had success mixing Classic style names into an SLDS style sheet?

(This is not a duplicate of What is suggested approach to transfer VF pages to be lightning ready. The answers there say nothing about the specifics of restyling tags such as apex:pageBlock and apex:sectionHeader.)

PS

Below is a copy of the Trailhead section referenced by crmprogdev. Interesting that the word "fantastic" is used in that; Google's first definition of that word is:

imaginative or fanciful; remote from reality

Three alternatives are proposed in the document:

  1. A complete re-write to use explicit SLDS classes and idioms: amounts to re-doing work that has already been done and doubling the on-going maintenance and testing costs for the UI.
  2. Add explicit SLDS classes to every Visualforce tag via the styleClass attribute (and probably conditionally render some tags) and somehow ensure the SLDS styles win over the default ones; conditionally include the new style sheet so the pages work for both Classic styling and SLDS.
  3. Leverage the CSS classes emitted by Visualforce (which presumably are changing little now given the focus on Lightning) so only a conditional style sheet include needs adding.

Option 3 looks like the obvious way to go from the development point of view where the aim is "good enough" styling, but is being discouraged.

I'm surprised that no-one has posted an answer here. Sharing good ideas in this area could save us all a lot of pain.

Styling Strategies and Recommendations

In the current release there’s only one supported method for creating
Visualforce pages that match the Lightning Experience visual design,
and that’s to create new pages using the Lightning Design System.
Before we get to specifics, let’s think at a higher level and consider
the different strategies for applying Lightning Experience styling to
your pages. In particular, let’s talk about your existing pages.

There are two ways to affect the styling of existing pages to make
them look more like Lightning Experience. (First Bullet Point) Change the markup to apply
new styling—make changes in your pages. (Second bullet point) Change the styling rules for
existing markup—make changes in your stylesheets. These aren’t either
/ or. You can use them individually or in combination.

The Lightning Design System is a fantastic all-new toolkit for styling
your pages, and we’ll talk about it in detail shortly. Correctly using
the Lightning Design System means using the Lightning Design System
stylesheets with all-new markup for your Visualforce pages. Again,
this is the only supported method for matching the Lightning
Experience visual design.

However, it is possible to add the Lightning Design System
stylesheets, and revise your pages to use them. How much work this is
depends on how closely you want to match Lightning Experience as well
as the specific markup and components in your code. While it’s
possible to achieve decent results this way, it’s not an approach we
recommend. The Lightning Design System was designed to be applied to
specific markup, and that’s simply not what Visualforce emits. There’s
an “impedance mismatch” that, while not fatal, is definitely a serious
rock in your shoe when you take this path.

Finally, there’s the other approach: adding new rules and styles to
your existing (or a new) stylesheet to make your existing markup look
more like Lightning Experience. If your page is already mostly styled
with your own stylesheets, this approach might work well for you. If
instead you’re mostly using the built-in Visualforce components and
the Salesforce Classic styling, it requires you to override the styles
from the Salesforce Classic stylesheet.

While this is technically possible, we want to discourage you from
taking this approach. It introduces dependencies into your markup and
styles that you don’t want to have. These dependencies are on the
structure, IDs, and classes of the HTML rendered by the built-in
Visualforce components. We want to be really clear here: the HTML
rendered by the built-in Visualforce components is an internal
implementation detail, subject to change without notice. If you have
dependencies on it in your own stylesheets, your styling will
eventually break.

Eric comment TTT REALLY? No-one else has any input at all?? I would think that someone else is dealing with this?

Best Answer

So here is my two cents

Note Per comments this does not seem to completely answer the question as OP is looking to keep classic look while providing a look that also appears well under Lightning using a single solution.

For VF components that you want to style as SLDS without having to modify the controller you need to de-componetize them. Adding styles after the DOM has been rendered as pointed out above is not great because it relies on implementation level details you have no control over. Not to mention it is just hacky...

Also, VF components just do a lot of the work for us. Unfortunately, with SLDS we are again going to need to do much of the HTML work ourselves but it is not really that bad.

Using a template approach like mentioned What is suggested approach to transfer VF pages to be lightning ready

So the idea for the code you posted would be to:

  1. Pageblocktable becomes an HTML table
  2. Use an apex repeat to build the table rows using the controller variable
  3. Use an input/output field with the appropriate slds styleclass in each table row

You can maintain controller and page functionality while doing this.

Bottom line is you will need to break these compound tags into HTML elements. Simple tags like input field can be used as is with the appropriate styleclass and surrounding HTML elements thus allowing you to keep built in functionality

Original Markup

<apex:page standardController="Contact" recordSetVar="cs">
    <div class="slds cv">
        <apex:form>
            <apex:sectionHeader title="{!$ObjectType.Contact.labelPlural}" subtitle="All"/>
            <apex:pageMessages />
            <apex:pageBlock title="{!$ObjectType.Contact.labelPlural}">
                <apex:pageBlockTable value="{!cs}" var="c">
                    <apex:column value="{!c.FirstName}"/>
                    <apex:column value="{!c.LastName}"/>
                    <apex:column value="{!c.Birthdate}"/>
                    <apex:column value="{!c.CreatedDate}"/>
                </apex:pageBlockTable>
            </apex:pageBlock>
        </apex:form>
    </div>
</apex:page>

Output

enter image description here

SLDS Pageblock / Pageblocktable Markup

A bit more markup but the way it is done ensures that it continues working and does not rely on the "implementation level details" of the old VF components

<apex:page id="dummySLDSPage" standardController="Contact" recordSetVar="cs" showHeader="false"
           standardStylesheets="false" applyHtmlTag="false" applyBodyTag="false" docType="html-5.0">

    <head>

        <apex:stylesheet
                value="{!URLFOR($Resource.SLDS, 'assets/styles/salesforce-lightning-design-system-vf.min.css')}"/>

    </head>


    <div class="SLDS">

        <body>

        <apex:form>
            <apex:pageMessages/>

            <!-- Replaces the Standard Pageblock Table Header-->
            <div class="slds-page-header" role="banner" xmlns="http://www.w3.org/2000/svg"
                 xmlns:xlink="http://www.w3.org/1999/xlink">
                <div class="slds-grid">
                    <div class="slds-col">
                        <div class="slds-media slds-no-space slds-grow">
                            <div class="slds-media__figure">
                                <svg aria-hidden="true" class="slds-icon slds-icon-standard-contact">
                                    <use xlink:href="{!URLFOR($Resource.AppFrontier_Assets, '/assets/icons/standard-sprite/svg/symbols.svg#contact')}"></use>
                                </svg>
                            </div>
                            <div class="slds-media__body">
                                <p class="slds-text-title--caps slds-line-height--reset">{!$ObjectType.Contact.labelPlural}</p>
                                <p class="slds-page-header__title slds-truncate"
                                   title="ALL">ALL</p>
                            </div>
                        </div>
                    </div>

                </div>
            </div>

            <!-- Replaces the Pageblocktable Table -->
            <table class="slds-table slds-table--bordered slds-table--cell-buffer">
                <!-- Use a caption if desired
                    <caption class="slds-text-heading&#45;&#45;label slds-p-bottom&#45;&#45;medium">{!$ObjectType.Contact.labelPlural}</caption>
                -->
                <thead>
                <tr class="slds-text-title--caps">
                    <th scope="col">
                        <div class="slds-truncate" title="{!$ObjectType.Contact.fields.FirstName.Label}">
                                {!$ObjectType.Contact.fields.FirstName.Label}
                        </div>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="{!$ObjectType.Contact.fields.LastName.Label}">
                                {!$ObjectType.Contact.fields.LastName.Label}
                        </div>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="{!$ObjectType.Contact.fields.BirthDate.Label}">
                                {!$ObjectType.Contact.fields.BirthDate.Label}
                        </div>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="{!$ObjectType.Contact.fields.CreatedDate.Label}">
                                {!$ObjectType.Contact.fields.CreatedDate.Label}
                        </div>
                    </th>

                </tr>

                </thead>
                <tbody>
                <apex:repeat value="{!cs}" var="c"> <!-- Build the columns -->
                    <tr>
                        <th scope="row" data-label="{!$ObjectType.Contact.fields.FirstName.Label}">
                            <div class="slds-truncate" title="{!c.FirstName}">
                                <apex:outputField value="{!c.FirstName}"/> <!-- Standard VF component -->
                            </div>
                        </th>
                        <td data-label="{!c.LastName}">
                            <div class="slds-truncate"
                                 title="{!c.LastName}">
                                <apex:outputField value="{!c.LastName}"/>
                            </div>
                        </td>
                        <td data-label="{!c.Birthdate}">
                            <div class="slds-truncate"
                                 title="{!c.Birthdate}">
                                <apex:outputField value="{!c.Birthdate}"/>
                            </div>
                        </td>
                        <td data-label="{!c.CreatedDate}">
                            <div class="slds-truncate"
                                 title="{!c.CreatedDate}">
                                <apex:outputField value="{!c.CreatedDate}"/>
                            </div>
                        </td>
                    </tr>
                </apex:repeat>
                </tbody>
            </table>

        </apex:form>


        </body>

    </div>
</apex:page>

Output

enter image description here

The general idea would apply to the rest of the items you would like to convert. You could even go so far as spending a bit of time to make VF Components that are done in SLDS styling and used to replace existing VF components. If the project is large this could be beneficial as it would save a lot of time.

Maybe I should put a package of reusable replacement SLDS components for VF components together...hmm...There is already a library out there but it had way too much overhead and really hacked the styling.

Some additional resources:

VF SLDS Checkbox: SLDS and inputField checkbox

VF SLDS Lookup with autocomplete: Lookup Field Dual Keyboard Focus (Answered with working Autocomplete lookup component and JS example for VF/SLDS)

VF SLDS Select Checkboxes: Convert apex:selectCheckboxes into slds style

VF Page Messages Replacement: How to convert a Visualforce apex:pageMessages to be lightning style

Related Topic