BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
gdb02
Fluorite | Level 6

The "Green Blue" color looks red on my monitor, but "Blue Green" looks similar to cyan, as expected.

SAS documentation (from %HELPCLR(CNS);)  says: 3. The hues may be combined in either order. Blue Green is the same as Green Blue. A hue with the "ish" suffix must be listed first, as in Yellowish Green.

 

SAS code to show this (ran in SAS 9.4 TS1M8, Windows 10):

data test;
colorname='Green Blue'; output;
colorname='Blue Green'; output;
run;
proc report data=test;
  title1 'What is going on with BLUE GREEN named color combinations?';
  column  colorname colorname=clr; 
  define colorname/ 'Color Name';
  define clr  / 'Color';
  compute clr /char length=200; 
     call   define(_col_,'style','style={background='||colorname||'}');
  endcomp;
run;

 

@carpentera

(code adapted from  https://blogs.sas.com/content/sgf/2016/08/03/tips-for-working-with-color-names-formats-macros-ods-ex...

Additional reference here:  https://support.sas.com/documentation/cdl/en/graphref/63022/HTML/default/viewer.htm#colors-specify-c...

1 ACCEPTED SOLUTION

Accepted Solutions
Quentin
Super User

@gdb02 wrote:
Great idea, but I was going for code readability over color consistency for the project I was working on.

For readability, I sometimes do stuff like:

 

%let blue = cx3D5AAE;
%let green = cx90B328;

So then in code &blue or &green is more readable than the hex codes. 

 

And if I really want to confuse myself, I can remap &green to resolve to a different color without changing my reporting code.  : )

The Boston Area SAS Users Group is hosting free webinars!
Next webinar will be in January 2025. Until then, check out our archives: https://www.basug.org/videos. And be sure to subscribe to our our email list.

View solution in original post

12 REPLIES 12
Cynthia_sas
SAS Super FREQ

Hi:

  I was always taught to add the "ish" to the first color if I wanted to mix colors. However, over the years, I've started not using words for colors and have migrated to using hex color codes which I get from an online HTML color picker.

  Here's a modified version of your program, where you see that sometimes swapping the color names work (like Red Yellow/Yellow Red), but sometimes it doesn't work like with Blue Green and Green Blue:

Cynthia_sas_2-1733247081475.png

 

  So I tried Greenish Blue next, but...  Honestly, Greenish Blue doesn't look all that "greenish" to me, so when I hear "green blue" I think teal and the RGB hex code for teal is 008080, and that RGB hex value does look like teal to me in the output:

Cynthia_sas_1-1733246841792.png

  If you look at the RGB codes that were used in the ODS HTML output file, you can see exactly what codes were used for each colorname. So, although I can't explain why you get #FF0000 when you specify Green Blue as the color, the workaround is to bite the bullet and find the exact hex code for the color you want. This is the web site I use to find HTML RGB color values: https://www.w3schools.com/colors/colors_picker.asp and I like it because it shows all the different shades lighter to darker if I want to do a gradient:

Cynthia_sas_3-1733247288266.png

 

  I hope this helps you come up with the a way to specify colors that works for you.

Cynthia

FreelanceReinh
Jade | Level 19

Hello @gdb02,

 

The %CNS function seems to work correctly on those color names:

95    %put %CNS(Blue Green) %HLS2RGB(%CNS(Blue Green));
H12C80FF CX00FFFF
96    %put %CNS(Green Blue) %HLS2RGB(%CNS(Green Blue));
H12C80FF CX00FFFF
97    %put %CNS(Greenish Blue) %HLS2RGB(%CNS(Greenish Blue));
H14A80FF CX007FFF

So, another workaround is to use it via RESOLVE in your CALL DEFINE statement:

call define(_col_,'style','style={background='||resolve('%CNS('||colorname||')')||'}');

 

Tom
Super User Tom
Super User

That is not actually a function in the macro language.  Instead it is just normal macro.  SAS included it in the text file that is loaded by the SASAUTOS process when you first call the %COLORMAC macro.  So to make use of it make sure to first include a call to that macro.

 

I know the SAS Graph documentation calls that a "macro function" but it is just a normal macro call.  But since the macro does not generate any semicolons (more specifically no semicolons that are not macro quoted) you can use it in the middle of a statement, like your %PUT statements.  So the macro call kind of works like a function call.

 

gdb02
Fluorite | Level 6

Great idea.  It seems the %CNS macro requires "%colormac;".  Also, it won't resolve some color names I would expect it to, such as "Very Light Strong Purpleish Blue" or "Light Vivid Blueish Green" or "Dark Vivid Redish Orange".

FreelanceReinh
Jade | Level 19

@gdb02 wrote:

It seems the %CNS macro requires "%colormac;". 


Yes, you're right. Sorry, I had incorrectly assumed that the %HELPCLR macro (which you mentioned in your initial post) requires %COLORMAC as well, but in fact it doesn't even mention that requirement in the help text about %CNS.

 


@gdb02 wrote:

Also, it won't resolve some color names I would expect it to, such as "Very Light Strong Purpleish Blue" or "Light Vivid Blueish Green" or "Dark Vivid Redish Orange".


I think it does. It is just very picky about spelling:

2     %put %CNS(Very Light Strong Purplish Blue);
H00FD4BF
3     %put %CNS(Light Vivid Bluish Green);
H10EABFF
4     %put %CNS(Dark Vivid Reddish Orange);
H0965EFF

 

GraphGuy
Meteorite | Level 14

If you care about your colors, and want them to look consistent no matter what type of output you're creating (png, svg, java, activex, javaimg, actximg, pdf, etc) ... I suggest always using the hex rgb color codes.

gdb02
Fluorite | Level 6
Great idea, but I was going for code readability over color consistency for the project I was working on.
Quentin
Super User

@gdb02 wrote:
Great idea, but I was going for code readability over color consistency for the project I was working on.

For readability, I sometimes do stuff like:

 

%let blue = cx3D5AAE;
%let green = cx90B328;

So then in code &blue or &green is more readable than the hex codes. 

 

And if I really want to confuse myself, I can remap &green to resolve to a different color without changing my reporting code.  : )

The Boston Area SAS Users Group is hosting free webinars!
Next webinar will be in January 2025. Until then, check out our archives: https://www.basug.org/videos. And be sure to subscribe to our our email list.
Tom
Super User Tom
Super User

@Quentin wrote:

@gdb02 wrote:
Great idea, but I was going for code readability over color consistency for the project I was working on.

And if I really want to confuse myself, I can remap &green to resolve to a different color without changing my reporting code.  : )


Which seems to be what SAS has done, I assume by accident, replacing the color code for GREEN BLUE with the color code for RED.

Someone should raise this as a bug report to SAS technical support team.

data test;
  input colorname $20.;
cards;
RED
BLUE
GREEN
YELLOW
BLUE GREEN
GREEN BLUE
;
proc report data=test;
  title1 'What is going on with BLUE GREEN named color combinations?';
  column  colorname colorname=clr; 
  define colorname/ 'Color Name';
  define clr  / 'Color';
  compute clr /char length=200; 
     call   define(_col_,'style','style={background='||colorname||'}');
  endcomp;
run;

Tom_0-1733772945364.png

 

ballardw
Super User

I always found having a concrete example of output was helpful in picking colors. So with that here is a program that makes most of the interesting CNS names and generates a table with the color text and a cell filled with that color.

 

proc format;
value lightness
1='Black'     
2='Very Dark' 
3='Dark'      
4='Medium'    
5='Light'     
6='Very Light'
7='White'
;
value Saturation
1='Gray'      
2='Grayish'   
3='Moderate'  
4='Strong'    
5='Vivid'
; 
value Hue 
1='Blue'
2='Purple'
3='Red'
4='Orange'
5='Yellow'
6='Green'
0=' '
;
run;

data cfmttest ;
   length text $40.;
   fmtname = 'CNSColor';
   do l=2 to 6; /* skip black and white as they cannot combine  */
   do s=2 to 5; /*GRAY is a color and cannot combine with others*/
   do h1=0 to 6;
   do ish= 0 to 1;
   do h2=1 to 6;
      if h1=h2 then goto skip; /* double color doesn't make sense*/
      if 2 <= abs(h2-h1) <5 then goto skip; /* shades of colors only allowed is sequential combinations*/
      if 1 <= (h1-h2) and ish=1 then goto skip; /* ISH shades only allowed on lower order*/
      l_= strip(put(l,lightness.));
      s_= strip(put(s,saturation.));
      if h1>0 and ish=1 then do;
         h1_= cats(put(h1,hue.),'ish');
         select (h1_);
            when ('Blueish') h1_='Bluish';
            when ('Redish')  h1_='Reddish';
            when ('Orangeish') h1_='Orangish';
            when ('Purpleish') h1_='Purplish';
            otherwise;
         end;
      end;
      else h1_ = strip(put(h1,hue.));
      h2_=strip(put(h2,hue.));
      text= catx(' ', l_,s_,h1_,h2_);
      output;
      skip: ;
   end;

   end;
   end;
   end;
   end;


run;

/* above logic generates duplicates and I'm too lazy to try to remove them */
proc sort data=cfmttest nodupkey; by text;run;
%colormac();    /*sas supplied macro to make color naming macros available*/
data cfmtest2;
   set cfmttest (drop= l_ s_ h1_ h2_);
   by text;
   retain start 0;
   if first.text then do;
      start=start+1;
      end=start;
      output;
   end;
run;

data cfmtest3;
   set cfmtest2;
   length label $ 10;
   label=resolve('%cns ('||text||')');
run;

proc format cntlin = cfmtest3 library=work;
run;

Proc tabulate data=cfmtest3;
   class text;
   var start;
   table text='',
         start=' '*sum*f=best5.*{style={background=cnsColor. color=cnscolor. width=.5in}} /box='CNS name';
   keylabel sum=' ';
   title 'CNS Color Example';
run;title;

White and Black are not included as CNS with any combination of those isn't very interesting. Also the various Gray as a color are not very entertaining.

Some code to make the text has spelling corrections and uses the %CNS macro to create a shorter and consistent codes for a Format to assign colors.

 

Having some output you can print or see if the color is usable. I've seen instances of the color is okay on a screen but printed not so hot.

 

And using the text created in the above program we can demonstrate that other uses of "Blue Green" and "Green Blue" can yield some pretty surprising results:

proc report data=cfmtest2;
  column  text text=clr; 
  define text/ 'Color Name';
  define clr  / 'Color';
  compute clr /char length=200; 
     call   define(_col_,'style','style={background='||text||'}');
  endcomp;
run;
Ksharp
Super User

You could yield all the color name via the following code. Pick up one of them you liked.

filename _colors TEMP;    /* create a temporary text file */
 
/* write text file with colors from the registry */
proc registry startat='HKEY_SYSTEM_ROOT\COLORNAMES' list export=_colors;
run; 





  data RegistryRGB;
   infile _colors end=eof;         /* read from text file; last line sets EOF flag to true */
   input;                          /* read one line at a time into _infile_ */
 
   length ColorName $32 hex $8; 
   retain hex "CX000000";
   s = _infile_;
   k = findw(s, 'hex:');          /* does the string 'hex' appear? */
   if k then do;                  /* this line contains a color */
      i = findc(s, '=', 2);       /* find the second quotation mark (") */
      ColorName = substr(s, 2, i-3);            /* name is between the quotes */
      /* build up the hex value from a comma-delimited value like 'FA,EB,D7' */
      substr(hex, 3, 2) = substr(s, k+5 , 2);
      substr(hex, 5, 2) = substr(s, k+8 , 2);
      substr(hex, 7, 2) = substr(s, k+11, 2);
 
      R = inputn(substr(hex, 3, 2), "HEX2."); /* get RGB coordinates from hex */
      G = inputn(substr(hex, 5, 2), "HEX2.");
      B = inputn(substr(hex, 7, 2), "HEX2.");
   end;
   if k;
   drop k i s;
run;






options nodate ;
title;
proc report data=work.RegistryRGB nowd;
  column  colorname colorname=clr; 
  define colorname/ 'Color Name';
  define clr  / 'Color';
  compute clr /char length=35; 
     call   define(_col_,'style','style={background='||colorname||'}');
  endcomp;
  run;

Ksharp_0-1733448625262.png

 

Ksharp
Super User

You also could use build-in macro to get the color you want.

%paint(values=1 to 5,colors=white red)
proc report data=colors nowd;
compute _rgb_;
call define(_col_,'style',cats('style={background=',_rgb_,'}'));
endcomp;
run;

Ksharp_0-1733448907224.png

 

 

 

%paint(values=1 to 5,colors=blue green)
proc report data=colors nowd;
compute _rgb_;
call define(_col_,'style',cats('style={background=',_rgb_,'}'));
endcomp;
run;

Ksharp_1-1733448946366.png

 

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 12 replies
  • 820 views
  • 9 likes
  • 8 in conversation