Thursday, September 22, 2016

Apply lightning design system (LDS) to existing visualforce pages

Disclaimer: General recommendation from salesforce - For existing pages - you don’t try to adapt them to match the visual design of Lightning Experience. There are two reasons for this. First, Lightning Experience is still evolving, and matching its styling yourself means you’re chasing a moving target. That’s work. Second, it’s even more work if you don’t have the tools to do it. In the current release, the tools are mostly not there. We have a number of ideas here and, Safe Harbor, we’re already hard at work at bringing them to you in a future release. So if you can wait, that’s our recommendation.

How to apply LDS to my existing visualforce pages
OK before we start modifying our code, lets first review few things to remember
  • <apex:pageblock> and <apex:inputField> are not supported with LDS, so unless you want your VF pages to be mix of LDS and classic view you need to refactor your code to avoid these elements.
  • If you are planning to use SVG Spritemap icons, add the attribute xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" to the <html> element or the parent <div> element
  • If using SVG spritemap image icons with IE, use the svg4everybody script

Now that we know what to look for lets try it out
First lets consider an existing VF page, this page creates account and contact record.

AccountContactVF

 <apex:page controller="AccountContactController">  
   <!-- ADD SECTION HEADER -->  
   <apex:sectionHeader title="Demo VF Page" subtitle="Create Account and Contact"/>  
   <apex:form >  
   <!-- CREATE PAGE BLOCK -->  
     <apex:pageBlock mode="edit">  
         <!-- SECTION TO CREATE ACCOUNT -->  
         <apex:pageBlockSection title="Account Info" columns="2">  
              <apex:inputField value="{!acc.Name}"></apex:inputField>  
           <apex:inputField value="{!acc.Type}"></apex:inputField>  
           <apex:inputField value="{!acc.ShippingStreet}"></apex:inputField>  
           <apex:inputField value="{!acc.ShippingCity}"></apex:inputField>  
           <apex:inputField value="{!acc.ShippingState}"></apex:inputField>  
           <apex:inputField value="{!acc.ShippingCountry}"></apex:inputField>  
           <apex:inputField value="{!acc.Shippingpostalcode}"></apex:inputField>  
         </apex:pageBlockSection>  
         <!-- SECTION TO CREATE CONTACT -->  
         <apex:pageBlockSection title="Contact Info" columns="2">  
              <apex:inputField value="{!con.FirstName}"></apex:inputField>  
           <apex:inputField value="{!con.LastName}"></apex:inputField>  
           <apex:inputField value="{!con.HomePhone}"></apex:inputField>  
           <apex:inputField value="{!con.MobilePhone}"></apex:inputField>  
         </apex:pageBlockSection>  
         <!-- ADD BUTTONS -->  
         <apex:pageBlockButtons >  
           <apex:commandButton value="Clear"/>  
           <apex:commandButton value="Save" action="{!saveAccCont}"/>  
         </apex:pageBlockButtons>  
     </apex:pageBlock>  
   </apex:form>  
 </apex:page>  


Lets take a quick look at the above VF page, just by looking at the page we can see that for the page to render properly we need make the following changes.

Must Have or Kind Of Must Have Changes:
  • apex:pageblock needs to be replaced with slds-panel
  • apex:inputfield needs to be replaced with apex:inputtext, apex:selectlist etc
  • apex:pageblocksection needs to be replaced with slds-panel__section
  • apex:pageblockbuttons needs to be replaced with a combination of slds-docked-form-footer and slds-button-group
  • Since we would be replacing few apex:inputField with apex:selectlist, we need to update the controller to handle the picklist values.
Optional or Nice To Have Changes:
  • Use slds-form--compound to allow better grouping of fields
  • Use inline icon to stylize text fields
  • Replace apex:pagesectionheader with slds-global-header_container

After making the necessary changes our VF page code will look like:

AccountContactLds

 <apex:page controller="AccountContactController" applyBodyTag="false" docType="html-5.0">  
   <head>  
     <!-- CUSTOM GENERATED STYLE SHEET -->  
     <apex:stylesheet value="{!URLFOR($Resource.SLDS212, 'assets/styles/salesforce-lightning-design-system-vf.css')}" />  
   </head>   
   <body>  
     <!-- REQUIRED SLDS WRAPPER - CUSTOM SCOPING CLASS USED WHEN GENERATING CUSTOM CSS -->  
     <div class="trailhead-lightning">  
       <!-- ADD GLOBAL HEADER -->  
       <header class="slds-global-header_container">  
         <!-- ADD GLOBAL HEADER ICON -->  
         <!-- ADD FLOT AND SPACING TO HAVE BOTH ICON AND TEXT IN SAME LINE -->  
         <div class="slds-col slds-float--left slds-m-right--small">  
           <!-- ADD ICON -->  
           <div class="slds-icon slds-icon_container slds-icon-standard-account slds-icon--medium">  
             <img src="{!URLFOR($Resource.SLDS212,'/assets/icons/standard/account_60.png')}" alt="" />    
           </div>   
         </div>  
         <!-- ADD GLOBAL HEADER TEXT -->  
         <div class="slds-col ">  
           <!-- ADD TEXT -->  
           <div class="slds-page-header__title slds-truncate">  
             Create Account and Contact  
           </div>  
         </div>  
       </header>  
       <!-- ADD PANEL -->  
       <div class="slds-panel slds-grid slds-grid--vertical slds-nowrap slds-form--compound" aria-labelledby="newaccountform">  
         <apex:form >  
           <!-- ADD SECTION GROUPS -->  
           <div class="slds-panel__section">  
             <legend class="slds-form-element__label slds-text-title--caps">ACCOUNT INFO</legend>  
             <div class="form-element__group">  
               <!-- ADD ROW -->  
               <div class="slds-form-element__row">  
                 <!-- ADD FIELDS -->  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Account Name</apex:outputLabel>  
                   <apex:inputText value="{!acc.Name}" styleClass="slds-form-element__control slds-input"/>  
                 </div>  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Account Type</apex:outputLabel>  
                   <apex:inputText value="{!acc.Type}" styleClass="slds-form-element__control slds-input"> </apex:inputText>  
                 </div>  
               </div>  
               <!-- ADD ROW -->  
               <div class="slds-form-element__row">  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Shipping Street</apex:outputLabel>  
                   <apex:inputTextarea value="{!acc.ShippingStreet}" styleClass="slds-form-element__control slds-textarea"/>  
                 </div>  
               </div>  
               <!-- ADD ROW -->  
               <div class="slds-form-element__row">  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Shipping City</apex:outputLabel>  
                   <apex:inputText value="{!acc.ShippingCity}" styleClass="slds-form-element__control slds-input"/>  
                 </div>  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Shipping State</apex:outputLabel>  
                   <apex:inputText value="{!acc.ShippingState}" styleClass="slds-form-element__control slds-input"/>  
                 </div>  
               </div>  
               <!-- ADD ROW -->  
               <div class="slds-form-element__row">  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Shipping Country</apex:outputLabel>  
                   <apex:selectList value="{!acc.ShippingCountry}" styleClass="slds-form-element__control slds-select slds-select_container" size="1">  
                        <apex:selectOptions value="{!countryList}"></apex:selectOptions>  
                   </apex:selectList>  
                 </div>  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Shipping Postal Code</apex:outputLabel>  
                   <apex:inputText value="{!acc.ShippingPostalCode}" styleClass="slds-form-element__control slds-input"/>  
                 </div>  
               </div>  
              </div>  
           </div>  
           <!-- SECTION GROUP ENDS-->  
           <!-- ADD SECTION GROUPS -->  
           <div class="slds-panel__section">  
             <legend class="slds-form-element__label slds-text-title--caps">CONTACT INFO</legend>  
             <div class="form-element__group">  
               <!-- ADD ROW -->  
               <div class="slds-form-element__row">  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">First Name</apex:outputLabel>  
                   <apex:inputText value="{!con.FirstName}" styleClass="slds-form-element__control slds-input"/>  
                 </div>  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">  
                     <abbr class="slds-required" title="required">*</abbr>   
                     Last Name  
                   </apex:outputLabel>  
                   <apex:inputText value="{!con.LastName}" styleClass="slds-form-element__control slds-input"/>  
                 </div>  
               </div>  
               <!-- ADD ROW -->  
               <div class="slds-form-element__row" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Phone Number</apex:outputLabel>  
                   <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon--left">  
                     <svg aria-hidden="true" class="slds-input__icon">  
                      <use xlink:href="{!URLFOR($Resource.SLDS212,'/assets/icons/utility-sprite/svg/symbols.svg#call')}"></use>  
                     </svg>  
                     <apex:inputText value="{!con.HomePhone}" styleClass="slds-input" html-placeholder="(333) 333-3333"/>  
                   </div>  
                 </div>  
                 <div class="slds-form-element">  
                   <apex:outputLabel styleClass="slds-form-element__label">Mobile Number</apex:outputLabel>  
                   <apex:inputText value="{!con.MobilePhone}" styleClass="slds-form-element__control slds-input" html-placeholder="(444) 444-4444"/>  
                 </div>  
               </div>  
             </div>  
           </div>  
           <!-- FOOTER -->  
           <div class="slds-panel__section">  
             <div class="slds-docked-form-footer slds-button-group slds-float--right" role="group">  
               <apex:commandButton value="Clear" styleClass="slds-button slds-button--neutral"/>  
                     <apex:commandButton value="Save" action="{!saveAccCont}" styleClass="slds-button slds-button--brand"/>  
             </div>  
           </div>  
         </apex:form>  
       </div>  
     </div>  
   </body>  
 </apex:page>  

Supporting Apex Controller

 public class AccountContactController {  
   public Account acc{get;set;}  
   public Contact con{get;set;}  
   public AccountContactController()  
   {  
     acc = new Account();  
     con = new Contact();  
   }  
   public pageReference saveAccCont()  
   {  
     PageReference pg = null;  
     if(acc != null)  
     {  
       insert acc;  
       if(con != null && acc.Id != null)  
       {  
         con.AccountId = acc.Id;  
         insert con;  
         pg = new PageReference('/'+acc.Id);  
         return pg;  
       }  
       else  
       {  
         return pg;  
       }  
     }  
     else  
     {  
          return pg;  
     }  
   }  
   public List<SelectOption> getCountryList()  
   {  
     List<SelectOption> countryList = new List<SelectOption>();  
     countryList.add(new SelectOption('US','US'));  
     countryList.add(new SelectOption('CANADA','CANADA'));  
     countryList.add(new SelectOption('MEIXCO','MEIXCO'));  
     return countryList;  
   }  
 }  

Output
As you can see its quite a bit of work to apply lds to existing VF pages. However if you want to align the look and feel of your VF pages with lightning experience its well worth it.

Screenshots
Classic VF Page
LDS VF Page

1 comment:

  1. Thanks for the Informative blog.
    It helped me a lot in building the solution for my client.

    ReplyDelete