Left-arm spinner Rangana Herath picked up 5-79, and match figures of 9-159, as the tourists bowled the hosts out for 241, with nine balls left in the day, to claim their first win in nine Tests in South Africa.
The victory was Sri Lanka’s first in 16 Tests stretching back to the middle of last year while the visitors levelled the three-Test series 1-1 with the deciding match scheduled to start in Cape Town on January 3.
The day was a disaster for South Africa as they crashed to 133 for six before a 99-run seventh-wicket stand between AB de Villiers and Dale Steyn (43) added a touch of gloss to the scoreboard.
The match seemed set to go into the fifth day as dark clouds gathering over the ground made the possibility of bad light halting play a reality.
But De Villiers, who scored a stubborn 69 off 141 balls, his 27th Test fifty, was finally dismissed when he was trapped leg-before by Herath.
De Villiers’ dismissal heralded the beginning of the end for the Proteas and they lost their last three wickets in the space of four balls.
Off-spinner Tillakaratne Dilshan, who claimed his first Test win as captain, trapped Morne Morkel (5) lbw before Herath mopped up the tail by removing Steyn leg-before and bowling Marchant de Lange (0).The Proteas had lost their first wicket when Graeme Smith (26) was caught at second slip after being forced to play at a well-directed bouncer from Dilhara Fernando.
The hosts then endured a miserable middle session as they lost five wickets for 50 runs. Jacques Rudolph (22) departed in the over after lunch when he drove loosely at a delivery from seamer Thisara Perera with Mahela Jayawardene taking a superb diving catch at second slip.
Jacques Kallis (0) then collected the first pair of his Test career when his attempted sweep shot to a Herath delivery saw the ball take a top-edge on its way to bouncing off Kallis’ helmet before nestling into Tharanga Paranavitana’s hands at short leg.
Kallis was initially given not out but umpire Steve Davis’ on-field decision was overturned on review.
Hashim Amla had looked in fine touch as he raced to his 20th Test fifty, off 77 balls with five fours, but with his score on 51 Amla was run out after he called Ashwell Prince (7) through for a quick single.Prince turned his back on Amla who tried his best to regain his ground at the keeper’s end but Chanaka Welegedara’s throw to keeper Dinesh Chandimal found Amla well short.
Prince was the next man out when he edged a lifting delivery from Fernando to Paranavitana at first slip before Mark Boucher (7) was trapped leg-before by Herath.
Steyn had earlier completed his 17th five-wicket haul in his 50th Test as the Proteas bowled Sri Lanka out for 279, 35 minutes after the start of play as the tourists established a lead of 449 runs.
Steyn grabbed 5-73 off 20 overs as he picked up the wickets of Perera (12), well caught by a diving Kallis at second slip, and Chanaka Welegedara (10), caught at square leg.
Morkel then ended the innings when he had Fernando (3) caught in the gully after the batsman fended at a brutal short ball.
Teams
South Africa - Graeme Smith (captain), Jacques Rudolph, Hashim Amla, Jacques Kallis, AB de Villiers, Ashwell Prince, Mark Boucher (wk), Dale Steyn, Morne Morkel, Imran Tahir, Marchant de Lange
Sri Lanka - Tillakaratne Dilshan (captain), Tharanga Paranavitana,
Kumar Sangakkara, Mahela Jayawardene, Thilan Samaraweera, Angelo
Mathews, Dinesh Chandimal (wk), Thisara Perera, Rangana Herath, Dilhara
Fernando, Chanaka Welegedara