Salesforce Developers Blog

Visual Contacts with Visualforce & CSS3

Avatar for Quinton WallQuinton Wall
Visualforce is a fantastic container for other web libraries and frameworks such as jQuery, CSS3, and HTML5. Here is a unique way to display Contacts within Salesforce.
Visual Contacts with Visualforce & CSS3
December 20, 2011
Listen to this article
0:00 / 0:00

I’ve been working on code and content updates to our Force.com Architecture Workshop over the past few weeks. Today I was working on the Visualforce section. It’s easy to forget that Visualforce is a container for just about any web library you can think of. Too often we get stuck on the 80+ tags provided by Salesforce. But in the world of jQuery, HTML5, CSS3 etc, we can do some really cool things.

I’ll be the first to admit I am not a ‘scripting’ expert, but it doesn’t take much work to combine some style, with Salesforce data to produce something like this:

The effect is pretty cool, and amazingly simple to do. Let’s start with the controller extension. There is nothing fancy here, just grab all the Contacts for a particular Account:

public class AccountsVisualContacts {

    public Account acct {get; set;}
    public Id acctid {get; set;}
    public List<Contact> allContacts { get; set;}

     public AccountsVisualContacts(ApexPages.StandardController stdController) {
        this.acct = (Account)stdController.getRecord();
         allContacts = [select id, Name, FirstName, LastName, Wiki_Link__c, Image_URL__c from Contact where Accountid = :acct.id];
    }
}

Now, we can add the Visualforce page. I ‘borrowed’ the CSS from the net, and added it to the top of my Visualforce page. The rest of the code is leveraging the styles defined within an apex:repeat tag. The only other trick here to include a variable to dynamically increment a counter to allow me to reference the correct CSS style. For this simple example, I am using a predefined number of styles, but there is no reason I could not use the same incrementing variable trick to dynamically set the width and height of my polaroids.

You will also notice that I am using the standard Account controller, and my custom controller as an extension. Using the standard Account controller allows me to link directly to my Visualforce page from a custom button on the Account layout. We’ll see why this is important a bit later. I also added a custom field on the Contact object, Image_URL__c to point to an image source on the internet (specifically, images from wikipedia as we can be pretty comfortable they are not going anywhere)

<apex:page standardController="Account" extensions="AccountsVisualContacts" sidebar="false" >

    <style type="text/css">

    body {
        background-color: #E9E9E9;
        color: #333;
        font-family:"Lucida handwriting", "Snell Roundhand", "Helvetica Neue",Arial,Helvetica,sans-serif;
        font-size: 16px;
    }

    .amp {
        font-family:Garamond,Baskerville,Georgia,serif !important;
        font-style:italic;
        font-weight:normal;
        border: none;
    }

    a.polaroid {
        display: block;
        text-decoration: none;
        color: #333;
        padding: 10px 10px 20px 10px;
        width: 150px;
        border: 1px solid #BFBFBF;
        background-color: white;
        z-index: 2;
        font-size: 0.7em;
        -webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        -moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        -webkit-transition: -webkit-transform 0.5s ease-in;
    }
    a.polaroid:hover,
    a.polaroid:focus,
    a.polaroid:active {
        z-index: 999;
        border-color: #6A6A6A;
        -webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        -moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        -webkit-transform: rotate(0deg);
        -moz-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    .polaroid img {
        margin: 0 0 15px;
        width: 150px;
        height: 150px;
    }

    a img {
        border: none;
        display: block;
    }

    .photo-album {
        position: relative;
        width: 80%;
        margin: 0 auto;
        max-width: 70em;
        height: 450px;
        margin-top: 5em;
        min-width: 800px;
        max-width: 900px;
    }
    .photo-album .polaroid {
        position: absolute;
    }
    .photo-album h1 {
        position: absolute;
        z-index: 5;
        top: 150px;
        text-align: center;
        width: 100%;
        line-height: 1.9;
    }
    .photo-album h1 span {
        background-color: white;
        font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;
        padding: 0.4em 0.8em 0.3em 0.8em;
        -webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        -moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
        border: 1px solid #6A6A6A;
    }
    .photo-album .small {
        width: 75px;
        padding: 6px 6px 12px 6px;
        font-size: 0.6em;
    }
    .photo-album .small img {
        width: 75px;
        height: 75px;
    }
    .photo-album .medium {
        width: 200px;
        padding: 13px 13px 26px 13px;
        font-size: 0.8em;
    }
    .photo-album .medium img {
        width: 200px;
        height: 200px;
    }
    .photo-album .large {
        width: 300px;
        padding: 20px 20px 30px 20px;
        font-size: 1em;
    }
    .photo-album .large img {
        width: 300px;
        height: 300px;
    }
    .photo-album .img1 {
        bottom: 10px;
        right: 365px;
        -webkit-transform: rotate(10deg);
        -moz-transform: rotate(10deg);
        transform: rotate(10deg);
    }
    .photo-album .img2 {
        top: 50px;
        right: 20px;
        -webkit-transform: rotate(-4deg);
        -moz-transform: rotate(-4deg);
        transform: rotate(-4deg);
    }
    .photo-album .img3 {
        left: 400px;
        top: 0;
        -webkit-transform: rotate(-5deg);
        -moz-transform: rotate(-5deg);
        transform: rotate(-5deg);
    }
    .photo-album .img4 {
        top: 10px;
        left: 495px;
        -webkit-transform: rotate(-20deg);
        -moz-transform: rotate(-20deg);
        transform: rotate(-20deg);
    }
    .photo-album .img5 {
        bottom: 0;
        right: 0;
        -webkit-transform: rotate(1deg);
        -moz-transform: rotate(1deg);
        transform: rotate(1deg);
    }
    .photo-album .img6 {
        bottom: 10px;
        right: 156px;
        -webkit-transform: rotate(6deg);
        -moz-transform: rotate(6deg);
        transform: rotate(6deg);
    }
    .photo-album .img7 {
        bottom:0;
        left:400px;
        -webkit-transform: rotate(-10deg);
        -moz-transform: rotate(-10deg);
        transform: rotate(-10deg);
    }
    .photo-album .img8 {
        bottom: -20px;
        left: 700px;
        -webkit-transform: rotate(-8deg);
        -moz-transform: rotate(-8deg);
        transform: rotate(-8deg);
    }
    .photo-album .img9 {
        bottom: 0;
        left: 0;
        -webkit-transform: rotate(-8deg);
        -moz-transform: rotate(-8deg);
        transform: rotate(-8deg);
    }
    .photo-album .img10 {
        top: 0;
        left: 20px;
        -webkit-transform: rotate(8deg);
        -moz-transform: rotate(8deg);
        transform: rotate(8deg);
    }

    a:hover,
    a:focus {
        z-index: 5;
    }

    </style>

    <div>

        <h1><span>Contacts of {!account.name}</span></h1>

        <apex:variable var="count" value="{!1}"/>
        <apex:repeat value="{!allContacts}" var="c">

        <apex:outputLink value="/{!c.id}" styleClass="medium polaroid img{!Round(count,0)}"
         target="_blank"><img src="{!c.Image_URL__C}" alt="{!c.Name}" />{!c.Name}</apex:outputLink>

        <apex:variable var="count" value="{!count + 1}"/>
        </apex:repeat>

    </div>

</apex:page>

Ok, all the hard stuff is done. All that is left is to add a custom button to our Account Details, and point it to the Visualforce page:


That’s it. Load this into your Salesforce org, and you are good to go!

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

Add to Slack Subscribe to RSS