21

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?

  • In addition to the linked question, Specific to your question, you would not use PageBlock any longer, you would change it to a section, form, etc to meet your styling needs. PageBlock Tables would simply become a LDS Data Table using an apex:repeat to build the <tr> Some of the VF components would need to be reworked as they are not relevant to LDS – Eric Nov 18 '16 at 18:31
  • @Eric I've made the question more specific. I'm looking for pragmatic CSS solutions here. Having to add logic to remove (presumably by conditionally rendering) parts of the (many pages) is not a very attractive option. But if it really is unavoidable and not too hard to do and you've been through all this for a large set of pages then please do answer about your experience. – Keith C Nov 18 '16 at 18:36
  • Not been through it on a large scale, however, from what I have done, those VF components do not really exist is LDS. Additionally, it seems that standard style sheets do not play well with LDS so I have to exclude them which again, negates the ability to use standard styling of those components. There are some very clever people here, including yourself, so will let them answer. My comments only serve to get your mindset ready for what may be a lot of work ahead for you. Also, you do not have to conditionally render as the LDS will display fine in classic env but will look like LDS – Eric Nov 18 '16 at 18:41
  • Related: salesforce.stackexchange.com/questions/100267/… with an answer from Peter – Eric Nov 18 '16 at 18:43
  • 2
    Haha, been watching this today so I could pace a bounty lol. Glad you did. Can I add to the bounty? – Eric Nov 20 '16 at 19:44
8
+50

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

  • Eric, thanks for this detailed answer. As all our managed package customers are still using Classic, I'm looking for "continue to work in Classic and look OK in Lightning Experience". Are you suggesting using the SLDS styled page in both or maintaining two separate pages? – Keith C Nov 26 '16 at 15:16
  • @KeithC - the above will work in both. So I am saying just the one page. But if you must keep the look and feel of classic then you would have to do two and I would not advise that. Are you trying to keep the classic look for classic? – Eric Nov 26 '16 at 15:20
  • Eric, yes I am. We have thousands of users that are likely to stay in Classic and them seeing major visual changes that offer no benefit seems like a bad idea. My bias is to look after those customers and do something pragmatic for new customers who want to use LEX. When LEX is a more significant part of the market, we would look to build first class native LEX components. – Keith C Nov 26 '16 at 15:31
  • @KeithC - Ok. my apologies, I did not realize that. Then you may have to do two pages and render conditionally. BUT that provides its own set of issues when it comes to a managed package...My answer is strictly converting to LEX and thus does not answer your question. I will delete it here shortly.... – Eric Nov 26 '16 at 15:34
  • Don't delete it; I think all discussion on this area is helpful. And no-one else seems willing to share. You could add a small edit to qualify the case it does cover. – Keith C Nov 26 '16 at 15:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.